diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/modules/main/obj/main.d b/modules/main/obj/main.d new file mode 100644 index 0000000..ac4fef1 --- /dev/null +++ b/modules/main/obj/main.d @@ -0,0 +1,8 @@ +obj/main.o: src/main.c ../../include/kc_memory.h ../../include/kc.h \ + ../../include/kc_windows.h ../../include/kc_list.h \ + ../../include/kc_macro.h +../../include/kc_memory.h: +../../include/kc.h: +../../include/kc_windows.h: +../../include/kc_list.h: +../../include/kc_macro.h: diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/modules/main/obj/main.d b/modules/main/obj/main.d new file mode 100644 index 0000000..ac4fef1 --- /dev/null +++ b/modules/main/obj/main.d @@ -0,0 +1,8 @@ +obj/main.o: src/main.c ../../include/kc_memory.h ../../include/kc.h \ + ../../include/kc_windows.h ../../include/kc_list.h \ + ../../include/kc_macro.h +../../include/kc_memory.h: +../../include/kc.h: +../../include/kc_windows.h: +../../include/kc_list.h: +../../include/kc_macro.h: diff --git a/modules/main/obj/main.o b/modules/main/obj/main.o new file mode 100644 index 0000000..e2dcc49 --- /dev/null +++ b/modules/main/obj/main.o Binary files differ diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/modules/main/obj/main.d b/modules/main/obj/main.d new file mode 100644 index 0000000..ac4fef1 --- /dev/null +++ b/modules/main/obj/main.d @@ -0,0 +1,8 @@ +obj/main.o: src/main.c ../../include/kc_memory.h ../../include/kc.h \ + ../../include/kc_windows.h ../../include/kc_list.h \ + ../../include/kc_macro.h +../../include/kc_memory.h: +../../include/kc.h: +../../include/kc_windows.h: +../../include/kc_list.h: +../../include/kc_macro.h: diff --git a/modules/main/obj/main.o b/modules/main/obj/main.o new file mode 100644 index 0000000..e2dcc49 --- /dev/null +++ b/modules/main/obj/main.o Binary files differ diff --git a/modules/main/src/lang_block_parser.cpp b/modules/main/src/lang_block_parser.cpp new file mode 100644 index 0000000..2b1b98a --- /dev/null +++ b/modules/main/src/lang_block_parser.cpp @@ -0,0 +1,12 @@ + + +class LangBlockParser +{ + public: + LangBlockParser(); + virtual ~LangBlockParser(); + private: +}; + + + diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/modules/main/obj/main.d b/modules/main/obj/main.d new file mode 100644 index 0000000..ac4fef1 --- /dev/null +++ b/modules/main/obj/main.d @@ -0,0 +1,8 @@ +obj/main.o: src/main.c ../../include/kc_memory.h ../../include/kc.h \ + ../../include/kc_windows.h ../../include/kc_list.h \ + ../../include/kc_macro.h +../../include/kc_memory.h: +../../include/kc.h: +../../include/kc_windows.h: +../../include/kc_list.h: +../../include/kc_macro.h: diff --git a/modules/main/obj/main.o b/modules/main/obj/main.o new file mode 100644 index 0000000..e2dcc49 --- /dev/null +++ b/modules/main/obj/main.o Binary files differ diff --git a/modules/main/src/lang_block_parser.cpp b/modules/main/src/lang_block_parser.cpp new file mode 100644 index 0000000..2b1b98a --- /dev/null +++ b/modules/main/src/lang_block_parser.cpp @@ -0,0 +1,12 @@ + + +class LangBlockParser +{ + public: + LangBlockParser(); + virtual ~LangBlockParser(); + private: +}; + + + diff --git a/modules/main/src/main.c b/modules/main/src/main.c new file mode 100644 index 0000000..d416ff6 --- /dev/null +++ b/modules/main/src/main.c @@ -0,0 +1,71 @@ +#include +#include + +#include + +#include + +extern KcList* KcArrayList_new_ArrayList(size_t size, int cap); + +bool handler(const char* data) +{ + printf("%s\n", data); + return true; +} + +typedef struct +{ + int val1; + int val2; +} VVV; + +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + VVV v1 = { .val1 = 1, .val2 = 11 }; + VVV v2 = { .val1 = 2, .val2 = 22 }; + VVV v3 = { .val1 = 3, .val2 = 33 }; + VVV v4 = { .val1 = 4, .val2 = 44 }; + VVV v5 = { .val1 = 5, .val2 = 55 }; + + KcList* list = KcArrayList_new_ArrayList(sizeof(VVV), 5); + bool ret = list->add(list, list->size(list), &v1, 0); +printf("ret = %d\n", ret); + list->add(list, list->size(list), &v2, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v3, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v4, 0); +printf("size=%d\n", list->size(list)); + +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// + list->remove(list, 2, NULL, NULL); +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// +printf("size=%d\n", list->size(list)); + list->add(list, 1, &v5, 0); + + printf("-----\n"); + for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); + } + + + + return 0; +} diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/modules/main/obj/main.d b/modules/main/obj/main.d new file mode 100644 index 0000000..ac4fef1 --- /dev/null +++ b/modules/main/obj/main.d @@ -0,0 +1,8 @@ +obj/main.o: src/main.c ../../include/kc_memory.h ../../include/kc.h \ + ../../include/kc_windows.h ../../include/kc_list.h \ + ../../include/kc_macro.h +../../include/kc_memory.h: +../../include/kc.h: +../../include/kc_windows.h: +../../include/kc_list.h: +../../include/kc_macro.h: diff --git a/modules/main/obj/main.o b/modules/main/obj/main.o new file mode 100644 index 0000000..e2dcc49 --- /dev/null +++ b/modules/main/obj/main.o Binary files differ diff --git a/modules/main/src/lang_block_parser.cpp b/modules/main/src/lang_block_parser.cpp new file mode 100644 index 0000000..2b1b98a --- /dev/null +++ b/modules/main/src/lang_block_parser.cpp @@ -0,0 +1,12 @@ + + +class LangBlockParser +{ + public: + LangBlockParser(); + virtual ~LangBlockParser(); + private: +}; + + + diff --git a/modules/main/src/main.c b/modules/main/src/main.c new file mode 100644 index 0000000..d416ff6 --- /dev/null +++ b/modules/main/src/main.c @@ -0,0 +1,71 @@ +#include +#include + +#include + +#include + +extern KcList* KcArrayList_new_ArrayList(size_t size, int cap); + +bool handler(const char* data) +{ + printf("%s\n", data); + return true; +} + +typedef struct +{ + int val1; + int val2; +} VVV; + +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + VVV v1 = { .val1 = 1, .val2 = 11 }; + VVV v2 = { .val1 = 2, .val2 = 22 }; + VVV v3 = { .val1 = 3, .val2 = 33 }; + VVV v4 = { .val1 = 4, .val2 = 44 }; + VVV v5 = { .val1 = 5, .val2 = 55 }; + + KcList* list = KcArrayList_new_ArrayList(sizeof(VVV), 5); + bool ret = list->add(list, list->size(list), &v1, 0); +printf("ret = %d\n", ret); + list->add(list, list->size(list), &v2, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v3, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v4, 0); +printf("size=%d\n", list->size(list)); + +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// + list->remove(list, 2, NULL, NULL); +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// +printf("size=%d\n", list->size(list)); + list->add(list, 1, &v5, 0); + + printf("-----\n"); + for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); + } + + + + return 0; +} diff --git a/modules/main/src/main.cpp b/modules/main/src/main.cpp deleted file mode 100644 index 0dc5529..0000000 --- a/modules/main/src/main.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include - -#include -#include - - -class MyListener : public kcpp::MemoryListener -{ - public: - MyListener() {} - ~MyListener() {} - void notifyError(const kcpp::MemoryEntry& entry, const char* msg) - { - std::cout << entry.file << ":" << entry.line << "(" << entry.func << "):[size=" << entry.size << "] " - << msg << std::endl; - } - -}; - -bool handler(const kcpp::MemoryEntry& entry) -{ - std::cout << "# " << entry.file << ":" << entry.line << ":(" << entry.size << ")" << std::endl; - return false; -} - -struct alignas(256) OverAligned { - int val[8192]; - char cval[8192]; -}; -int main() -{ - - MyListener listener; - kcpp::MemoryManager::setListener(listener); - - char* tmp1 = new char; - char* tmp2 = new (std::nothrow) char; - OverAligned* tmp3 = new OverAligned; - tmp3->val[0] = 10; - tmp3->val[8000] = 8010; - tmp3->cval[0] = 'A'; - tmp3->cval[1] = '\0'; - std::cout << tmp3->cval << std::endl; - char* tmp4 = new char[5]; - tmp4[0] = 'X'; - tmp4[1] = 'Y'; - tmp4[2] = 'Z'; - tmp4[3] = '\0'; - char* ptr = static_cast(malloc(10)); - char* ptr2 = static_cast(malloc(20)); - char* ptr3 = static_cast(realloc(ptr2, 15)); - char* ptr4 = static_cast(realloc(tmp1, 55)); - -std::cout << "#####################################" << std::endl; -// kcpp::MemoryManager::entries(handler); - kcpp::MemoryManager::dump(std::cout, 4, true, true, 80); //, 100, true, true); -std::cout << "#####################################" << std::endl; - kcpp::MemoryManager::freeif(handler); -std::cout << "#####################################" << std::endl; - -// free(ptr3); -std::cout << "-- 1" << std::endl; - free(ptr4); -std::cout << "-- 2" << std::endl; - free(tmp2); -std::cout << "-- 3" << std::endl; - - std::cout << "========== delete OverAligned" << std::endl; - delete tmp3; - std::cout << "========== END delete OverAligned" << std::endl; -// operator delete(tmp3); - - std::cout << tmp4 << std::endl; - - std::cout << "========== delete tmp4[5]" << std::endl; - delete[] tmp4; - std::cout << "========== END delete tmp4[5]" << std::endl; - - (void)(ptr); - (void)(ptr2); - (void)(ptr3); - (void)(ptr4); - - char* tmp5 = new char[100]; - tmp5[0] = 'A'; - tmp5[1] = '\0'; - std::cout << tmp5[0] << std::endl; - delete [] tmp5; - - - return 0; - -} - diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/modules/main/obj/main.d b/modules/main/obj/main.d new file mode 100644 index 0000000..ac4fef1 --- /dev/null +++ b/modules/main/obj/main.d @@ -0,0 +1,8 @@ +obj/main.o: src/main.c ../../include/kc_memory.h ../../include/kc.h \ + ../../include/kc_windows.h ../../include/kc_list.h \ + ../../include/kc_macro.h +../../include/kc_memory.h: +../../include/kc.h: +../../include/kc_windows.h: +../../include/kc_list.h: +../../include/kc_macro.h: diff --git a/modules/main/obj/main.o b/modules/main/obj/main.o new file mode 100644 index 0000000..e2dcc49 --- /dev/null +++ b/modules/main/obj/main.o Binary files differ diff --git a/modules/main/src/lang_block_parser.cpp b/modules/main/src/lang_block_parser.cpp new file mode 100644 index 0000000..2b1b98a --- /dev/null +++ b/modules/main/src/lang_block_parser.cpp @@ -0,0 +1,12 @@ + + +class LangBlockParser +{ + public: + LangBlockParser(); + virtual ~LangBlockParser(); + private: +}; + + + diff --git a/modules/main/src/main.c b/modules/main/src/main.c new file mode 100644 index 0000000..d416ff6 --- /dev/null +++ b/modules/main/src/main.c @@ -0,0 +1,71 @@ +#include +#include + +#include + +#include + +extern KcList* KcArrayList_new_ArrayList(size_t size, int cap); + +bool handler(const char* data) +{ + printf("%s\n", data); + return true; +} + +typedef struct +{ + int val1; + int val2; +} VVV; + +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + VVV v1 = { .val1 = 1, .val2 = 11 }; + VVV v2 = { .val1 = 2, .val2 = 22 }; + VVV v3 = { .val1 = 3, .val2 = 33 }; + VVV v4 = { .val1 = 4, .val2 = 44 }; + VVV v5 = { .val1 = 5, .val2 = 55 }; + + KcList* list = KcArrayList_new_ArrayList(sizeof(VVV), 5); + bool ret = list->add(list, list->size(list), &v1, 0); +printf("ret = %d\n", ret); + list->add(list, list->size(list), &v2, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v3, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v4, 0); +printf("size=%d\n", list->size(list)); + +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// + list->remove(list, 2, NULL, NULL); +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// +printf("size=%d\n", list->size(list)); + list->add(list, 1, &v5, 0); + + printf("-----\n"); + for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); + } + + + + return 0; +} diff --git a/modules/main/src/main.cpp b/modules/main/src/main.cpp deleted file mode 100644 index 0dc5529..0000000 --- a/modules/main/src/main.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include - -#include -#include - - -class MyListener : public kcpp::MemoryListener -{ - public: - MyListener() {} - ~MyListener() {} - void notifyError(const kcpp::MemoryEntry& entry, const char* msg) - { - std::cout << entry.file << ":" << entry.line << "(" << entry.func << "):[size=" << entry.size << "] " - << msg << std::endl; - } - -}; - -bool handler(const kcpp::MemoryEntry& entry) -{ - std::cout << "# " << entry.file << ":" << entry.line << ":(" << entry.size << ")" << std::endl; - return false; -} - -struct alignas(256) OverAligned { - int val[8192]; - char cval[8192]; -}; -int main() -{ - - MyListener listener; - kcpp::MemoryManager::setListener(listener); - - char* tmp1 = new char; - char* tmp2 = new (std::nothrow) char; - OverAligned* tmp3 = new OverAligned; - tmp3->val[0] = 10; - tmp3->val[8000] = 8010; - tmp3->cval[0] = 'A'; - tmp3->cval[1] = '\0'; - std::cout << tmp3->cval << std::endl; - char* tmp4 = new char[5]; - tmp4[0] = 'X'; - tmp4[1] = 'Y'; - tmp4[2] = 'Z'; - tmp4[3] = '\0'; - char* ptr = static_cast(malloc(10)); - char* ptr2 = static_cast(malloc(20)); - char* ptr3 = static_cast(realloc(ptr2, 15)); - char* ptr4 = static_cast(realloc(tmp1, 55)); - -std::cout << "#####################################" << std::endl; -// kcpp::MemoryManager::entries(handler); - kcpp::MemoryManager::dump(std::cout, 4, true, true, 80); //, 100, true, true); -std::cout << "#####################################" << std::endl; - kcpp::MemoryManager::freeif(handler); -std::cout << "#####################################" << std::endl; - -// free(ptr3); -std::cout << "-- 1" << std::endl; - free(ptr4); -std::cout << "-- 2" << std::endl; - free(tmp2); -std::cout << "-- 3" << std::endl; - - std::cout << "========== delete OverAligned" << std::endl; - delete tmp3; - std::cout << "========== END delete OverAligned" << std::endl; -// operator delete(tmp3); - - std::cout << tmp4 << std::endl; - - std::cout << "========== delete tmp4[5]" << std::endl; - delete[] tmp4; - std::cout << "========== END delete tmp4[5]" << std::endl; - - (void)(ptr); - (void)(ptr2); - (void)(ptr3); - (void)(ptr4); - - char* tmp5 = new char[100]; - tmp5[0] = 'A'; - tmp5[1] = '\0'; - std::cout << tmp5[0] << std::endl; - delete [] tmp5; - - - return 0; - -} - diff --git a/modules/main/ut/Makefile b/modules/main/ut/Makefile new file mode 100644 index 0000000..cb97620 --- /dev/null +++ b/modules/main/ut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut.exe +TARGET = $(NAME) +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib -lkcpp -ldl + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/modules/main/obj/main.d b/modules/main/obj/main.d new file mode 100644 index 0000000..ac4fef1 --- /dev/null +++ b/modules/main/obj/main.d @@ -0,0 +1,8 @@ +obj/main.o: src/main.c ../../include/kc_memory.h ../../include/kc.h \ + ../../include/kc_windows.h ../../include/kc_list.h \ + ../../include/kc_macro.h +../../include/kc_memory.h: +../../include/kc.h: +../../include/kc_windows.h: +../../include/kc_list.h: +../../include/kc_macro.h: diff --git a/modules/main/obj/main.o b/modules/main/obj/main.o new file mode 100644 index 0000000..e2dcc49 --- /dev/null +++ b/modules/main/obj/main.o Binary files differ diff --git a/modules/main/src/lang_block_parser.cpp b/modules/main/src/lang_block_parser.cpp new file mode 100644 index 0000000..2b1b98a --- /dev/null +++ b/modules/main/src/lang_block_parser.cpp @@ -0,0 +1,12 @@ + + +class LangBlockParser +{ + public: + LangBlockParser(); + virtual ~LangBlockParser(); + private: +}; + + + diff --git a/modules/main/src/main.c b/modules/main/src/main.c new file mode 100644 index 0000000..d416ff6 --- /dev/null +++ b/modules/main/src/main.c @@ -0,0 +1,71 @@ +#include +#include + +#include + +#include + +extern KcList* KcArrayList_new_ArrayList(size_t size, int cap); + +bool handler(const char* data) +{ + printf("%s\n", data); + return true; +} + +typedef struct +{ + int val1; + int val2; +} VVV; + +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + VVV v1 = { .val1 = 1, .val2 = 11 }; + VVV v2 = { .val1 = 2, .val2 = 22 }; + VVV v3 = { .val1 = 3, .val2 = 33 }; + VVV v4 = { .val1 = 4, .val2 = 44 }; + VVV v5 = { .val1 = 5, .val2 = 55 }; + + KcList* list = KcArrayList_new_ArrayList(sizeof(VVV), 5); + bool ret = list->add(list, list->size(list), &v1, 0); +printf("ret = %d\n", ret); + list->add(list, list->size(list), &v2, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v3, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v4, 0); +printf("size=%d\n", list->size(list)); + +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// + list->remove(list, 2, NULL, NULL); +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// +printf("size=%d\n", list->size(list)); + list->add(list, 1, &v5, 0); + + printf("-----\n"); + for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); + } + + + + return 0; +} diff --git a/modules/main/src/main.cpp b/modules/main/src/main.cpp deleted file mode 100644 index 0dc5529..0000000 --- a/modules/main/src/main.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include - -#include -#include - - -class MyListener : public kcpp::MemoryListener -{ - public: - MyListener() {} - ~MyListener() {} - void notifyError(const kcpp::MemoryEntry& entry, const char* msg) - { - std::cout << entry.file << ":" << entry.line << "(" << entry.func << "):[size=" << entry.size << "] " - << msg << std::endl; - } - -}; - -bool handler(const kcpp::MemoryEntry& entry) -{ - std::cout << "# " << entry.file << ":" << entry.line << ":(" << entry.size << ")" << std::endl; - return false; -} - -struct alignas(256) OverAligned { - int val[8192]; - char cval[8192]; -}; -int main() -{ - - MyListener listener; - kcpp::MemoryManager::setListener(listener); - - char* tmp1 = new char; - char* tmp2 = new (std::nothrow) char; - OverAligned* tmp3 = new OverAligned; - tmp3->val[0] = 10; - tmp3->val[8000] = 8010; - tmp3->cval[0] = 'A'; - tmp3->cval[1] = '\0'; - std::cout << tmp3->cval << std::endl; - char* tmp4 = new char[5]; - tmp4[0] = 'X'; - tmp4[1] = 'Y'; - tmp4[2] = 'Z'; - tmp4[3] = '\0'; - char* ptr = static_cast(malloc(10)); - char* ptr2 = static_cast(malloc(20)); - char* ptr3 = static_cast(realloc(ptr2, 15)); - char* ptr4 = static_cast(realloc(tmp1, 55)); - -std::cout << "#####################################" << std::endl; -// kcpp::MemoryManager::entries(handler); - kcpp::MemoryManager::dump(std::cout, 4, true, true, 80); //, 100, true, true); -std::cout << "#####################################" << std::endl; - kcpp::MemoryManager::freeif(handler); -std::cout << "#####################################" << std::endl; - -// free(ptr3); -std::cout << "-- 1" << std::endl; - free(ptr4); -std::cout << "-- 2" << std::endl; - free(tmp2); -std::cout << "-- 3" << std::endl; - - std::cout << "========== delete OverAligned" << std::endl; - delete tmp3; - std::cout << "========== END delete OverAligned" << std::endl; -// operator delete(tmp3); - - std::cout << tmp4 << std::endl; - - std::cout << "========== delete tmp4[5]" << std::endl; - delete[] tmp4; - std::cout << "========== END delete tmp4[5]" << std::endl; - - (void)(ptr); - (void)(ptr2); - (void)(ptr3); - (void)(ptr4); - - char* tmp5 = new char[100]; - tmp5[0] = 'A'; - tmp5[1] = '\0'; - std::cout << tmp5[0] << std::endl; - delete [] tmp5; - - - return 0; - -} - diff --git a/modules/main/ut/Makefile b/modules/main/ut/Makefile new file mode 100644 index 0000000..cb97620 --- /dev/null +++ b/modules/main/ut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut.exe +TARGET = $(NAME) +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib -lkcpp -ldl + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/main/ut/src/ut_kc_memory_dump.cpp b/modules/main/ut/src/ut_kc_memory_dump.cpp new file mode 100644 index 0000000..b07eec7 --- /dev/null +++ b/modules/main/ut/src/ut_kc_memory_dump.cpp @@ -0,0 +1,107 @@ +#include +#include + +#include + +using namespace kcpp; +using namespace kcpp::Assert; +using namespace kc; + +/** + * KcMemoryDump 単体テスト。 + */ +class UtKcMemoryDump: public TestCase +{ + public: + UtKcMemoryDump() : entry() + { /* NOP */ } + ~UtKcMemoryDump() {/* NOP */ } + + void setUp() + { + static char data_buff[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + entry.file = "file"; + entry.func = "func"; + entry.line = 123; + entry.size = 52; + entry.data = data_buff; + + snprintf(expected_info , sizeof(expected_info) , "file:123 (10 bytes) [func=func]"); + snprintf(expected_binary, sizeof(expected_binary), " | 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F"); + + } + + void testSample1() + { + int buff_size = 81;//sizeof(test_buff); + int bytes = 60; + bool binary = true; + bool ascii = true; + int column = 80; + + kc_memory_dump(test_buff, buff_size, &entry, bytes, binary, ascii, column); + + // カラム数が合致するか確認 + printf(" 10 20 30 40 50 60 70 80 90\n"); + printf("----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|\n"); + printf("%s[EOL]\n", test_buff); + } + void testSample2() + { + } + void run() + { + RUN_TEST(testSample1, "sample1"); + } + private: + KcMemoryEntry entry; + char test_buff[4096]; + char expected_info[4096]; + char expected_binary[4096]; + char expected_ascii[4096]; +}; + +#if 0 +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + char buff[1024]; + KcMemoryEntry entry = { + .file = "file", + .func = "func", + .line = 123, + .size = 10, + .data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + }; + + TestInfo testInfoList[] = + { + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = false, .max_column = 80 }, + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = true, .max_column = 80 }, + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = true, .max_column = 79 }, + + + /* 番兵 */ + { .buff_size = -1, .bytes = -1, .binary = false, .ascii = false, .max_column = -1 } + }; + + for (TestInfo* testInfo = &testInfoList[0]; testInfo->buff_size >= 0; testInfo++) + { + + kc_memory_dump(buff, testInfo->buff_size, &entry, testInfo->bytes, testInfo->binary, testInfo->ascii, testInfo->max_column); + + printf("####################################################\n"); + printf("buff_size = %d\n", testInfo->buff_size); + printf("bytes = %d\n", testInfo->bytes); + printf("max_column= %d\n", testInfo->max_column); + printf(" 10 20 30 40 50 60 70 80 90 \n"); + printf("----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|\n"); + printf("%s[END]\n", buff); + + } + return 0; +} +#endif diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/modules/main/obj/main.d b/modules/main/obj/main.d new file mode 100644 index 0000000..ac4fef1 --- /dev/null +++ b/modules/main/obj/main.d @@ -0,0 +1,8 @@ +obj/main.o: src/main.c ../../include/kc_memory.h ../../include/kc.h \ + ../../include/kc_windows.h ../../include/kc_list.h \ + ../../include/kc_macro.h +../../include/kc_memory.h: +../../include/kc.h: +../../include/kc_windows.h: +../../include/kc_list.h: +../../include/kc_macro.h: diff --git a/modules/main/obj/main.o b/modules/main/obj/main.o new file mode 100644 index 0000000..e2dcc49 --- /dev/null +++ b/modules/main/obj/main.o Binary files differ diff --git a/modules/main/src/lang_block_parser.cpp b/modules/main/src/lang_block_parser.cpp new file mode 100644 index 0000000..2b1b98a --- /dev/null +++ b/modules/main/src/lang_block_parser.cpp @@ -0,0 +1,12 @@ + + +class LangBlockParser +{ + public: + LangBlockParser(); + virtual ~LangBlockParser(); + private: +}; + + + diff --git a/modules/main/src/main.c b/modules/main/src/main.c new file mode 100644 index 0000000..d416ff6 --- /dev/null +++ b/modules/main/src/main.c @@ -0,0 +1,71 @@ +#include +#include + +#include + +#include + +extern KcList* KcArrayList_new_ArrayList(size_t size, int cap); + +bool handler(const char* data) +{ + printf("%s\n", data); + return true; +} + +typedef struct +{ + int val1; + int val2; +} VVV; + +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + VVV v1 = { .val1 = 1, .val2 = 11 }; + VVV v2 = { .val1 = 2, .val2 = 22 }; + VVV v3 = { .val1 = 3, .val2 = 33 }; + VVV v4 = { .val1 = 4, .val2 = 44 }; + VVV v5 = { .val1 = 5, .val2 = 55 }; + + KcList* list = KcArrayList_new_ArrayList(sizeof(VVV), 5); + bool ret = list->add(list, list->size(list), &v1, 0); +printf("ret = %d\n", ret); + list->add(list, list->size(list), &v2, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v3, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v4, 0); +printf("size=%d\n", list->size(list)); + +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// + list->remove(list, 2, NULL, NULL); +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// +printf("size=%d\n", list->size(list)); + list->add(list, 1, &v5, 0); + + printf("-----\n"); + for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); + } + + + + return 0; +} diff --git a/modules/main/src/main.cpp b/modules/main/src/main.cpp deleted file mode 100644 index 0dc5529..0000000 --- a/modules/main/src/main.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include - -#include -#include - - -class MyListener : public kcpp::MemoryListener -{ - public: - MyListener() {} - ~MyListener() {} - void notifyError(const kcpp::MemoryEntry& entry, const char* msg) - { - std::cout << entry.file << ":" << entry.line << "(" << entry.func << "):[size=" << entry.size << "] " - << msg << std::endl; - } - -}; - -bool handler(const kcpp::MemoryEntry& entry) -{ - std::cout << "# " << entry.file << ":" << entry.line << ":(" << entry.size << ")" << std::endl; - return false; -} - -struct alignas(256) OverAligned { - int val[8192]; - char cval[8192]; -}; -int main() -{ - - MyListener listener; - kcpp::MemoryManager::setListener(listener); - - char* tmp1 = new char; - char* tmp2 = new (std::nothrow) char; - OverAligned* tmp3 = new OverAligned; - tmp3->val[0] = 10; - tmp3->val[8000] = 8010; - tmp3->cval[0] = 'A'; - tmp3->cval[1] = '\0'; - std::cout << tmp3->cval << std::endl; - char* tmp4 = new char[5]; - tmp4[0] = 'X'; - tmp4[1] = 'Y'; - tmp4[2] = 'Z'; - tmp4[3] = '\0'; - char* ptr = static_cast(malloc(10)); - char* ptr2 = static_cast(malloc(20)); - char* ptr3 = static_cast(realloc(ptr2, 15)); - char* ptr4 = static_cast(realloc(tmp1, 55)); - -std::cout << "#####################################" << std::endl; -// kcpp::MemoryManager::entries(handler); - kcpp::MemoryManager::dump(std::cout, 4, true, true, 80); //, 100, true, true); -std::cout << "#####################################" << std::endl; - kcpp::MemoryManager::freeif(handler); -std::cout << "#####################################" << std::endl; - -// free(ptr3); -std::cout << "-- 1" << std::endl; - free(ptr4); -std::cout << "-- 2" << std::endl; - free(tmp2); -std::cout << "-- 3" << std::endl; - - std::cout << "========== delete OverAligned" << std::endl; - delete tmp3; - std::cout << "========== END delete OverAligned" << std::endl; -// operator delete(tmp3); - - std::cout << tmp4 << std::endl; - - std::cout << "========== delete tmp4[5]" << std::endl; - delete[] tmp4; - std::cout << "========== END delete tmp4[5]" << std::endl; - - (void)(ptr); - (void)(ptr2); - (void)(ptr3); - (void)(ptr4); - - char* tmp5 = new char[100]; - tmp5[0] = 'A'; - tmp5[1] = '\0'; - std::cout << tmp5[0] << std::endl; - delete [] tmp5; - - - return 0; - -} - diff --git a/modules/main/ut/Makefile b/modules/main/ut/Makefile new file mode 100644 index 0000000..cb97620 --- /dev/null +++ b/modules/main/ut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut.exe +TARGET = $(NAME) +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib -lkcpp -ldl + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/main/ut/src/ut_kc_memory_dump.cpp b/modules/main/ut/src/ut_kc_memory_dump.cpp new file mode 100644 index 0000000..b07eec7 --- /dev/null +++ b/modules/main/ut/src/ut_kc_memory_dump.cpp @@ -0,0 +1,107 @@ +#include +#include + +#include + +using namespace kcpp; +using namespace kcpp::Assert; +using namespace kc; + +/** + * KcMemoryDump 単体テスト。 + */ +class UtKcMemoryDump: public TestCase +{ + public: + UtKcMemoryDump() : entry() + { /* NOP */ } + ~UtKcMemoryDump() {/* NOP */ } + + void setUp() + { + static char data_buff[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + entry.file = "file"; + entry.func = "func"; + entry.line = 123; + entry.size = 52; + entry.data = data_buff; + + snprintf(expected_info , sizeof(expected_info) , "file:123 (10 bytes) [func=func]"); + snprintf(expected_binary, sizeof(expected_binary), " | 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F"); + + } + + void testSample1() + { + int buff_size = 81;//sizeof(test_buff); + int bytes = 60; + bool binary = true; + bool ascii = true; + int column = 80; + + kc_memory_dump(test_buff, buff_size, &entry, bytes, binary, ascii, column); + + // カラム数が合致するか確認 + printf(" 10 20 30 40 50 60 70 80 90\n"); + printf("----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|\n"); + printf("%s[EOL]\n", test_buff); + } + void testSample2() + { + } + void run() + { + RUN_TEST(testSample1, "sample1"); + } + private: + KcMemoryEntry entry; + char test_buff[4096]; + char expected_info[4096]; + char expected_binary[4096]; + char expected_ascii[4096]; +}; + +#if 0 +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + char buff[1024]; + KcMemoryEntry entry = { + .file = "file", + .func = "func", + .line = 123, + .size = 10, + .data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + }; + + TestInfo testInfoList[] = + { + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = false, .max_column = 80 }, + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = true, .max_column = 80 }, + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = true, .max_column = 79 }, + + + /* 番兵 */ + { .buff_size = -1, .bytes = -1, .binary = false, .ascii = false, .max_column = -1 } + }; + + for (TestInfo* testInfo = &testInfoList[0]; testInfo->buff_size >= 0; testInfo++) + { + + kc_memory_dump(buff, testInfo->buff_size, &entry, testInfo->bytes, testInfo->binary, testInfo->ascii, testInfo->max_column); + + printf("####################################################\n"); + printf("buff_size = %d\n", testInfo->buff_size); + printf("bytes = %d\n", testInfo->bytes); + printf("max_column= %d\n", testInfo->max_column); + printf(" 10 20 30 40 50 60 70 80 90 \n"); + printf("----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|\n"); + printf("%s[END]\n", buff); + + } + return 0; +} +#endif diff --git a/modules/main/ut/src/ut_main.cpp b/modules/main/ut/src/ut_main.cpp new file mode 100644 index 0000000..f1c3f90 --- /dev/null +++ b/modules/main/ut/src/ut_main.cpp @@ -0,0 +1,16 @@ +#include + +#include "ut_kc_memory_dump.cpp" + + +int main() +{ + UtKcMemoryDump memory_dump; + + memory_dump.run(); + + kcpp::utManager.printResult(); + + return 0; +} + diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/modules/main/obj/main.d b/modules/main/obj/main.d new file mode 100644 index 0000000..ac4fef1 --- /dev/null +++ b/modules/main/obj/main.d @@ -0,0 +1,8 @@ +obj/main.o: src/main.c ../../include/kc_memory.h ../../include/kc.h \ + ../../include/kc_windows.h ../../include/kc_list.h \ + ../../include/kc_macro.h +../../include/kc_memory.h: +../../include/kc.h: +../../include/kc_windows.h: +../../include/kc_list.h: +../../include/kc_macro.h: diff --git a/modules/main/obj/main.o b/modules/main/obj/main.o new file mode 100644 index 0000000..e2dcc49 --- /dev/null +++ b/modules/main/obj/main.o Binary files differ diff --git a/modules/main/src/lang_block_parser.cpp b/modules/main/src/lang_block_parser.cpp new file mode 100644 index 0000000..2b1b98a --- /dev/null +++ b/modules/main/src/lang_block_parser.cpp @@ -0,0 +1,12 @@ + + +class LangBlockParser +{ + public: + LangBlockParser(); + virtual ~LangBlockParser(); + private: +}; + + + diff --git a/modules/main/src/main.c b/modules/main/src/main.c new file mode 100644 index 0000000..d416ff6 --- /dev/null +++ b/modules/main/src/main.c @@ -0,0 +1,71 @@ +#include +#include + +#include + +#include + +extern KcList* KcArrayList_new_ArrayList(size_t size, int cap); + +bool handler(const char* data) +{ + printf("%s\n", data); + return true; +} + +typedef struct +{ + int val1; + int val2; +} VVV; + +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + VVV v1 = { .val1 = 1, .val2 = 11 }; + VVV v2 = { .val1 = 2, .val2 = 22 }; + VVV v3 = { .val1 = 3, .val2 = 33 }; + VVV v4 = { .val1 = 4, .val2 = 44 }; + VVV v5 = { .val1 = 5, .val2 = 55 }; + + KcList* list = KcArrayList_new_ArrayList(sizeof(VVV), 5); + bool ret = list->add(list, list->size(list), &v1, 0); +printf("ret = %d\n", ret); + list->add(list, list->size(list), &v2, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v3, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v4, 0); +printf("size=%d\n", list->size(list)); + +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// + list->remove(list, 2, NULL, NULL); +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// +printf("size=%d\n", list->size(list)); + list->add(list, 1, &v5, 0); + + printf("-----\n"); + for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); + } + + + + return 0; +} diff --git a/modules/main/src/main.cpp b/modules/main/src/main.cpp deleted file mode 100644 index 0dc5529..0000000 --- a/modules/main/src/main.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include - -#include -#include - - -class MyListener : public kcpp::MemoryListener -{ - public: - MyListener() {} - ~MyListener() {} - void notifyError(const kcpp::MemoryEntry& entry, const char* msg) - { - std::cout << entry.file << ":" << entry.line << "(" << entry.func << "):[size=" << entry.size << "] " - << msg << std::endl; - } - -}; - -bool handler(const kcpp::MemoryEntry& entry) -{ - std::cout << "# " << entry.file << ":" << entry.line << ":(" << entry.size << ")" << std::endl; - return false; -} - -struct alignas(256) OverAligned { - int val[8192]; - char cval[8192]; -}; -int main() -{ - - MyListener listener; - kcpp::MemoryManager::setListener(listener); - - char* tmp1 = new char; - char* tmp2 = new (std::nothrow) char; - OverAligned* tmp3 = new OverAligned; - tmp3->val[0] = 10; - tmp3->val[8000] = 8010; - tmp3->cval[0] = 'A'; - tmp3->cval[1] = '\0'; - std::cout << tmp3->cval << std::endl; - char* tmp4 = new char[5]; - tmp4[0] = 'X'; - tmp4[1] = 'Y'; - tmp4[2] = 'Z'; - tmp4[3] = '\0'; - char* ptr = static_cast(malloc(10)); - char* ptr2 = static_cast(malloc(20)); - char* ptr3 = static_cast(realloc(ptr2, 15)); - char* ptr4 = static_cast(realloc(tmp1, 55)); - -std::cout << "#####################################" << std::endl; -// kcpp::MemoryManager::entries(handler); - kcpp::MemoryManager::dump(std::cout, 4, true, true, 80); //, 100, true, true); -std::cout << "#####################################" << std::endl; - kcpp::MemoryManager::freeif(handler); -std::cout << "#####################################" << std::endl; - -// free(ptr3); -std::cout << "-- 1" << std::endl; - free(ptr4); -std::cout << "-- 2" << std::endl; - free(tmp2); -std::cout << "-- 3" << std::endl; - - std::cout << "========== delete OverAligned" << std::endl; - delete tmp3; - std::cout << "========== END delete OverAligned" << std::endl; -// operator delete(tmp3); - - std::cout << tmp4 << std::endl; - - std::cout << "========== delete tmp4[5]" << std::endl; - delete[] tmp4; - std::cout << "========== END delete tmp4[5]" << std::endl; - - (void)(ptr); - (void)(ptr2); - (void)(ptr3); - (void)(ptr4); - - char* tmp5 = new char[100]; - tmp5[0] = 'A'; - tmp5[1] = '\0'; - std::cout << tmp5[0] << std::endl; - delete [] tmp5; - - - return 0; - -} - diff --git a/modules/main/ut/Makefile b/modules/main/ut/Makefile new file mode 100644 index 0000000..cb97620 --- /dev/null +++ b/modules/main/ut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut.exe +TARGET = $(NAME) +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib -lkcpp -ldl + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/main/ut/src/ut_kc_memory_dump.cpp b/modules/main/ut/src/ut_kc_memory_dump.cpp new file mode 100644 index 0000000..b07eec7 --- /dev/null +++ b/modules/main/ut/src/ut_kc_memory_dump.cpp @@ -0,0 +1,107 @@ +#include +#include + +#include + +using namespace kcpp; +using namespace kcpp::Assert; +using namespace kc; + +/** + * KcMemoryDump 単体テスト。 + */ +class UtKcMemoryDump: public TestCase +{ + public: + UtKcMemoryDump() : entry() + { /* NOP */ } + ~UtKcMemoryDump() {/* NOP */ } + + void setUp() + { + static char data_buff[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + entry.file = "file"; + entry.func = "func"; + entry.line = 123; + entry.size = 52; + entry.data = data_buff; + + snprintf(expected_info , sizeof(expected_info) , "file:123 (10 bytes) [func=func]"); + snprintf(expected_binary, sizeof(expected_binary), " | 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F"); + + } + + void testSample1() + { + int buff_size = 81;//sizeof(test_buff); + int bytes = 60; + bool binary = true; + bool ascii = true; + int column = 80; + + kc_memory_dump(test_buff, buff_size, &entry, bytes, binary, ascii, column); + + // カラム数が合致するか確認 + printf(" 10 20 30 40 50 60 70 80 90\n"); + printf("----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|\n"); + printf("%s[EOL]\n", test_buff); + } + void testSample2() + { + } + void run() + { + RUN_TEST(testSample1, "sample1"); + } + private: + KcMemoryEntry entry; + char test_buff[4096]; + char expected_info[4096]; + char expected_binary[4096]; + char expected_ascii[4096]; +}; + +#if 0 +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + char buff[1024]; + KcMemoryEntry entry = { + .file = "file", + .func = "func", + .line = 123, + .size = 10, + .data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + }; + + TestInfo testInfoList[] = + { + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = false, .max_column = 80 }, + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = true, .max_column = 80 }, + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = true, .max_column = 79 }, + + + /* 番兵 */ + { .buff_size = -1, .bytes = -1, .binary = false, .ascii = false, .max_column = -1 } + }; + + for (TestInfo* testInfo = &testInfoList[0]; testInfo->buff_size >= 0; testInfo++) + { + + kc_memory_dump(buff, testInfo->buff_size, &entry, testInfo->bytes, testInfo->binary, testInfo->ascii, testInfo->max_column); + + printf("####################################################\n"); + printf("buff_size = %d\n", testInfo->buff_size); + printf("bytes = %d\n", testInfo->bytes); + printf("max_column= %d\n", testInfo->max_column); + printf(" 10 20 30 40 50 60 70 80 90 \n"); + printf("----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|\n"); + printf("%s[END]\n", buff); + + } + return 0; +} +#endif diff --git a/modules/main/ut/src/ut_main.cpp b/modules/main/ut/src/ut_main.cpp new file mode 100644 index 0000000..f1c3f90 --- /dev/null +++ b/modules/main/ut/src/ut_main.cpp @@ -0,0 +1,16 @@ +#include + +#include "ut_kc_memory_dump.cpp" + + +int main() +{ + UtKcMemoryDump memory_dump; + + memory_dump.run(); + + kcpp::utManager.printResult(); + + return 0; +} + diff --git a/modules/main/ut/src/ut_sample.cpp b/modules/main/ut/src/ut_sample.cpp new file mode 100644 index 0000000..72b5582 --- /dev/null +++ b/modules/main/ut/src/ut_sample.cpp @@ -0,0 +1,30 @@ +#include + +#include + +using namespace kcpp; + +class UtSample : public TestCase +{ + public: + UtSample() {} + ~UtSample() {} + void setUp() + { + std::cout << "setup" << std::endl; + } + void testSample1() + { + Assert::assertEquals(1,1); + } + void testSample2() + { + Assert::assertTrue(false); +// Assert::fail(); + } + void run() + { + RUN_TEST(testSample1, "sample1"); + RUN_TEST(testSample2, "sample2"); + } +}; diff --git a/include/kc_list_multi.h b/include/kc_list_multi.h new file mode 100644 index 0000000..8aa6590 --- /dev/null +++ b/include/kc_list_multi.h @@ -0,0 +1,167 @@ +/** + * @file kc_list_multi.h + * @brief List Multi モジュールヘッダファイル + */ +#ifndef KC_LIST_MULTI_H +#define KC_LIST_MULTI_H + +#include + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcListMulti +// + +/** + * 複数種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcListMulti_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + *// + int (*size)(struct KcListMulti_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcListMulti_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size element のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcListMulti_* list, const void element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素はコピーされて格納されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 挿入される要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcListMulti_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが、element に格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のサイズを指定します。削除に成功した場合、削除した要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcListMulti_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + */ + void (*sort)(struct KcListMulti_* list, + int (*comparator)(const void* element1, size_t size1, const void* element2, size_t size2)); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcListMulti_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param size 対象リスト内の指定された位置にある要素のサイズが格納されます。 + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcListMulti_* list, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。 + * 置換に成功した場合、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcListMulti_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcListMulti_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcListMulti_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + +} KcListMulti; + + + +#endif // KC_LIST_MULTI_H diff --git a/include/kcpp.hpp b/include/kcpp.hpp new file mode 100644 index 0000000..77bcad3 --- /dev/null +++ b/include/kcpp.hpp @@ -0,0 +1,11 @@ +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ +#ifndef KCPP_HPP +#define KCPP_HPP + +#include + + +#endif // KCPP_HPP diff --git a/include/kcpp_assert.hpp b/include/kcpp_assert.hpp new file mode 100644 index 0000000..2ff13b9 --- /dev/null +++ b/include/kcpp_assert.hpp @@ -0,0 +1,306 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ + AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ + AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ + AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ + virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ + const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ + const std::string& getFunc() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + + private: + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 + }; + + + namespace Assert + { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertNotNull(void* obj , const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + + + /** + * 指定された actual が、expected と同一でない場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + */ + #define assertEquals(expected, actual) assertEquals(expected, actual, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertTrue(condition) assertTrue(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された condition が、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + */ + #define assertFalse(condition) assertFalse(condition, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr でない場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNull(obj) assertNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 指定された obj が、nullptr の場合、AssertError を throw します。 + * + * @param obj 比較する値 + */ + #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) + + + /** + * 常に、AssertError を throw します。 + */ + #define assertFail() assertFail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/include/kcpp_dl.hpp b/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/include/kcpp_error.hpp b/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/include/kcpp_error.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// +#ifndef KCPP_ERROR_HPP +#define KCPP_ERROR_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復不能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Error: public Throwable + { + public: + Error() noexcept; + Error(const Error& t) noexcept; + Error(const std::string& msg) noexcept; + virtual ~Error() noexcept; + }; +} +#endif // KCPP_Error_HPP diff --git a/include/kcpp_exception.hpp b/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/include/kcpp_exception.hpp @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// +#ifndef KCPP_EXCEPTION_HPP +#define KCPP_EXCEPTION_HPP + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外クラス。 + * 回復可能なエラーの場合、本クラスを継承した例外を投げます。 + */ + class Exception : public Throwable + { + public: + Exception() noexcept; + Exception(const Exception& t) noexcept; + Exception(const std::string& msg) noexcept; + virtual ~Exception() noexcept; + }; +} +#endif // KCPP_EXCEPTION_HPP diff --git a/include/kcpp_memory.hpp b/include/kcpp_memory.hpp new file mode 100644 index 0000000..f8ba772 --- /dev/null +++ b/include/kcpp_memory.hpp @@ -0,0 +1,189 @@ +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KCPP_MEMORY_HPP +#define KCPP_MEMORY_HPP + +#include +#include +#include +#include +#include + +#include + + + +namespace kcpp { + + /** + * メモリ状態 + */ + enum MemoryMark + { + MEMORY_MARK_DELETED = 0x55AA0000, + MEMORY_MARK_ALLOCATED = 0x55AA1111, + MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, + MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 + }; + + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + int size; //!< 確保サイズ + MemoryMark _mark; //!< 確保メモリ状態 + MemoryEntry* _prev; //!< 前の管理メモリポインタ + MemoryEntry* _next; //!< 次の管理メモリポインタ + void* data; //!< データ + }; + + + /** + * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 + * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 + * メモリ確保, 解放, エラー発生時の通知を受信できます。 + * + * リスナの登録は、プログラム開始時に実施してください。 + */ + class MemoryListener + { + public: + MemoryListener(); + virtual ~MemoryListener(); + virtual void notifyAllocate(const MemoryEntry& entry); + virtual void notifyFree(const MemoryEntry& entry); + virtual void notifyError(const MemoryEntry& entry, const char* msg); + }; + + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, int mark, const char* file, const char* func, int line); + + + + /** + * メモリの確保、解放を管理します。 + */ + namespace MemoryManager + { + extern thread_local const char* file; + extern thread_local const char* func; + extern thread_local int line; + + void setListener(MemoryListener& listener); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); + void* malloc ( std::size_t size, const char* file, const char* func, int line); + void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); + void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); + void free (void* ptr); + } +} + + +#if (__cplusplus >= 202002L) +// C++20 (C++2a) +#define NODISCARD [[nodiscard]] +#else +#define NODISCARD +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 +// +// +#ifdef KCPP_MEMORY_ENABLED + +// C++17 (C++1z) 以降の new/delete 演算子 +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 +// +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// +// (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) +// NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; +// NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; +// +NODISCARD void* operator new(std::size_t size); +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + +NODISCARD void* operator new[](std::size_t size); +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; + + +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 +// +// 以下については、メモリ管理外としてオーバーライドしない。 +// (A) 配置 new で確保された記憶域の開放 +// void operator delete(void* ptr, void*) noexcept; +// void operator delete[](void* ptr, void*) noexcept; +// +void operator delete(void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::align_val_t alignment) noexcept; +void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +void operator delete[](void* ptr) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::align_val_t alignment) noexcept; +void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, std::size_t size) noexcept; +void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; + +#define new \ + ((kcpp::MemoryManager::file = __FILE__, \ + kcpp::MemoryManager::func = __func__, \ + kcpp::MemoryManager::line = __LINE__, \ + 0) && 0) ? 0 : new + +#define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) +#define free(ptr) kcpp::MemoryManager::free (ptr) + +#else +#include +#include + +#endif // KCPP_MEMORY_ENABLED + + +#endif // KC_MEMORY_HPP diff --git a/include/kcpp_throwable.hpp b/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/include/kcpp_throwable.hpp @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// +#ifndef KCPP_THROWABLE_HPP +#define KCPP_THROWABLE_HPP + +#include +#include + +#include + +namespace kcpp +{ + /** + * kcpp で扱う例外規定クラス。 + */ + class Throwable : public std::exception + { + public: + Throwable() noexcept; + Throwable(const Throwable& t) noexcept; + Throwable(const std::string& msg) noexcept; + virtual ~Throwable() noexcept; + virtual const char* what() const noexcept; + protected: + Throwable& operator=(const Throwable& t) = delete; + std::string message; + }; +} +#endif // KCPP_THROWABLE_HPP diff --git a/include/kcpp_unittest.hpp b/include/kcpp_unittest.hpp new file mode 100644 index 0000000..b011d66 --- /dev/null +++ b/include/kcpp_unittest.hpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP UNITTEST Header File +// +#ifndef KCPP_UNITTEST_HPP +#define KCPP_UNITTEST_HPP + +#include + +#include + + +namespace kcpp +{ + + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + +} + + +#endif // KCPP_UNITTEST_HPP diff --git a/lib/libkc.a b/lib/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/lib/libkc.a Binary files differ diff --git a/lib/libkcpp.a b/lib/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/lib/libkcpp.a Binary files differ diff --git a/modules/libkc/include/kc.h b/modules/libkc/include/kc.h index d19f7e8..4b42730 100644 --- a/modules/libkc/include/kc.h +++ b/modules/libkc/include/kc.h @@ -1,39 +1,49 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KC Header File -// +/** + * @file kc.h + * @brief Kantan C Library 共通ヘッダファイル。 + * @copyright 2001 - 2023 Nomura Kei + * @depends + * kc_windows.h + */ #ifndef KC_H #define KC_H -// ============================================================================= -// 共通定義 -// ============================================================================= + + +/** + * 指定された変数が未使用であることを明示します。 + * @param val 未使用変数 + */ #define UNUSED_VARIABLE(val) (void)(val) -#if defined(__cplusplus) && (__cplusplus >= 201103L) -// ============================================================================= -// C++11 -// ============================================================================= -#include -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) // ============================================================================= -// C11 +// C11 以降 // ============================================================================= #include #include -#include -#define thread_local _Thread_local -#define nullptr NULL + + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// ============================================================================= +// C++17 以降対応 +// ============================================================================= +#include + #else // ============================================================================= -// ERROR +// C11, C++17 より古い場合は、ERROR // ============================================================================= -#error "suuports C11/C++11 or later" +#error "suuports C11, C++17 or later" -#endif // C++11, C11, ERROR +#endif // C11, ERROR + + +#include + #endif // KC_H diff --git a/modules/libkc/include/kc_list.h b/modules/libkc/include/kc_list.h new file mode 100644 index 0000000..6f4a744 --- /dev/null +++ b/modules/libkc/include/kc_list.h @@ -0,0 +1,226 @@ +/** + * @file kc_list.h + * @brief List モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + * kc_macro.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include +#include + +typedef struct +{ +} KcIterator; + + + +/** + * 単一種類の要素を扱うことが可能なリスト。 + */ +typedef struct KcList_ +{ + + /** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ + int (*size)(struct KcList_* list); + + + /** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ + bool (*is_empty)(struct KcList_* list); + + + /** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ + bool (*contains)(struct KcList_* list, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ + bool (*add)(struct KcList_* list, int index, const void* element, size_t size); + + + /** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素のコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ + bool (*remove)(struct KcList_* list, int index, void* element, size_t* size); + + + /** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ + void (*sort)(struct KcList_* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); + + + /** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcList_* list); + + + /** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ + void* (*get)(struct KcList_* list, int index, size_t* size); + + + /** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ + bool (*set)(struct KcList_* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); + + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ + int (*index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ + int (*last_index_of)(struct KcList_* list, const void* element, size_t size); + + + /** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ + KcIterator* (*iterator)(struct KcList_* list, int index); + + + /** + * リスト管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void* _info; + + +} KcList; + + +/** + * サイズ固定の要素を管理する ArrayList を構築します。 + * + * @param element_size 要素のサイズ + * @param capacity 初期容量 + * @return ArrayList + */ +KcList* KcList_new_ArrayList(size_t element_size, int capacity); + + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList* KcList_new_LinkedList(void); + + +/** + * 渡されたポインタをそのまま要素として管理する LinkedList を構築します。 + * + * autofree が true の場合、 + * 次のメソッド呼び出し時に、不要となった要素のメモリを解放します。 + * - remove + * - clear + * - set + * また、リストに渡す要素は、malloc 等で確保された要素とする必要があります。 + * + * autofree が false の場合、 + * リスト内では要素のメモリ管理は実施せず、利用する側で管理する必要があります。 + * + * @param autofree true/false + */ +KcList* KcList_new_LinkedList_nocopy(bool autofree); + + +/** + * KcList を破棄します。 + * + * @param list 破棄するリスト + */ +void KcList_delete(KcList* list); + + +#endif // KC_LIST_H diff --git a/modules/libkc/include/kc_lock_guard.h b/modules/libkc/include/kc_lock_guard.h new file mode 100644 index 0000000..8bb70bd --- /dev/null +++ b/modules/libkc/include/kc_lock_guard.h @@ -0,0 +1,58 @@ +/** + * @file kc_lock_guard.h + * @brief KC ロックガードモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#ifndef KC_LOCK_GUARD_H +#define KC_LOCK_GUARD_H + +#include +#include + + +/** + * LockGuard 管理構造体 + */ +typedef struct +{ + mtx_t* mutex; +} KcLockGuard; + + +/** + * 指定された lock (mtx_t のポインタ)を用い、指定された区間ロックします。 + * 使用例) + * + * mtx_t mutex; + * mtx_init(&mutex, mtx_plain | mtx_recursive); + * + * + * kc_lock_guard(&mutex) { + * // この区間ロックが取得されている状態 + * // ブロックを抜けるとロックが自動解除される。 + * // [注意] 本ブロック内では、break, return, goto 等を利用しないでください。 + * } + */ +#define kc_lock_guard(lock) \ + for (KcLockGuard _guard = kc_lock_guard_init(lock); _guard.mutex != NULL; kc_lock_guard_release(&_guard), _guard.mutex = NULL) + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex); + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard); + + +#endif // KC_LOCK_GUARD_H diff --git a/modules/libkc/include/kc_macro.h b/modules/libkc/include/kc_macro.h new file mode 100644 index 0000000..246bb39 --- /dev/null +++ b/modules/libkc/include/kc_macro.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * + * 依存: なし + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_memory.h b/modules/libkc/include/kc_memory.h index 0408072..5e6f7c5 100644 --- a/modules/libkc/include/kc_memory.h +++ b/modules/libkc/include/kc_memory.h @@ -1,13 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @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 + #include + #ifdef __cplusplus extern "C" { namespace kc { @@ -15,77 +21,271 @@ #endif -#ifdef KC_MEMORY_ENABLED -// メモリ管理有効 -#define malloc(size) kc_memory_malloc ( size, __FILE__, __func__, __LINE__) -#define calloc(nmemb, size) kc_memory_calloc (nmemb, size, __FILE__, __func__, __LINE__) -#define realloc(ptr, size) kc_memory_realloc(ptr , size, __FILE__, __func__, __LINE__) -#define free(ptr) kc_memory_free (ptr) -#else -#include +// ============================================================================= +// KcMemoryMark +// ============================================================================= -#endif // !KC_MEMORY_ENABLED - - - -// 管理メモリ種別を表すための識別マーク -#define KC_MEMORY_MARK_HEAD (0x55AA5A00) -#define KC_MEMORY_MARK_MASK (0xFFFFFF00) +/** + * メモリ状態 + */ typedef enum { - KC_MEMORY_DELETED = KC_MEMORY_MARK_HEAD | 0x00, //!< メモリが解放されている - KC_MEMORY_ALLOCATED = KC_MEMORY_MARK_HEAD | 0x01, //!< メモリが確保されている - KC_MEMORY_ALLOCATED_NEW = KC_MEMORY_MARK_HEAD | 0x02, //!< new により確保されたメモリ - KC_MEMORY_ALLOCATED_NEW_ARRAY = KC_MEMORY_MARK_HEAD | 0x03 //!< new[] により確保されたメモリ + KC_MEMORY_DELETED = 0x55AA0000, //!< 解放済み + KC_MEMORY_ALLOCATED = 0x55AA1111, //!< 確保済み + KC_MEMORY_ALLOCATED_NEW = 0x55AA2222, //!< new により確保済み + KC_MEMORY_ALLOCATED_NEW_ARRAY = 0x55AA4444 //!< new[] により確保済み } KcMemoryMark; -/** - * ダンプサイズ - */ -#define KC_MEMORY_DUMP_SIZE (16) - /** - * 指定されたメモリ管理用種別マークが正しいか判定します。 + * 指定されたメモリ状態に対応する文字列表現を返します。 + * 返される文字列は、次の通り + * - alloc : malloc, calloc, realloc によりメモリが確保された + * - new : new によりメモリが確保された + * - new[] : new[] によりメモリが確保された + * - delete : 削除済みメモリ + * - other : 不明 * - * @param mark 種別マーク - * @return true/false (管理されているメモリ/管理されていないメモリ) + * @param mark メモリ状態 + * @return メモリ状態に対応する文字列表現 */ -#define kc_memory_is_valid_mark(mark) ((mark & KC_MEMORY_MARK_MASK) == SC_MEMORY_MARK_HEAD) +const char* KcMemoryMark_to_string(int mark); + +// ============================================================================= +// KcMemoryEntry +// ============================================================================= + /** * メモリエントリ。 */ typedef struct KcMemoryEntry_ { - const char* file; /*!< メモリ確保ファイル名 */ - const char* func; /*!< メモリ確保関数名 */ - int line; /*!< メモリ確保行番号 */ - int size; /*!< 確保サイズ */ - int _mark; /*!< 確保メモリ状態 */ - struct KcMemoryEntry_* _prev; /*!< 前の管理メモリポインタ */ - struct KcMemoryEntry_* _next; /*!< 次の管理メモリポインタ */ - void* data; /*!< データ */ + int size; //!< 確保サイズ + KcMemoryMark mark; //!< 確保メモリ状態 + const char* file; //!< メモリ確保ファイル名 + const char* func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + struct KcMemoryEntry_* _prev; //!< 前の管理メモリポインタ + struct KcMemoryEntry_* _next; //!< 次の管理メモリポインタ + void* data; //!< データ // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array) } KcMemoryEntry; -// ハンドラ関数ポインタ -typedef bool (*KcMemoryHandler)(KcMemoryEntry* entry, const char* msg); -// プロトタイプ宣言 -void kc_memory_set_handlers(KcMemoryHandler allocate, KcMemoryHandler free, KcMemoryHandler error); -bool kc_memory_entries(KcMemoryHandler handler); -bool kc_memory_freeif(KcMemoryHandler handler); -void kc_memory_dump(void); +// ============================================================================= +// KcMemoryListener +// ============================================================================= -// 以下は、通常直接使用しません。 -void* kc_memory_malloc ( size_t size, const char* file, const char* func, int line); -void* kc_memory_calloc (size_t nmemb, size_t size, const char* file, const char* func, int line); -void* kc_memory_realloc(void* ptr , size_t size, const char* file, const char* func, int line); -void kc_memory_free (void* ptr); +/** + * メモリ確保、解放、エラー発生時の通知用リスナ。 + */ +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 +#endif #ifdef __cplusplus diff --git a/modules/libkc/include/kc_memory_dump.h b/modules/libkc/include/kc_memory_dump.h new file mode 100644 index 0000000..3edd3d2 --- /dev/null +++ b/modules/libkc/include/kc_memory_dump.h @@ -0,0 +1,46 @@ +/** + * @file kc_memory_dump.h + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + * @depends + * kc.h + * kc_memory.h + * kc_memory.c + * kc_memory_dump.c + */ +#ifndef KC_MEMORY_DUMP_H +#define KC_MEMORY_DUMP_H + +#include + + +#ifdef __cplusplus +extern "C" { +namespace kc { +using namespace std; +#endif + + +/** + * 指定されたメモリエントリの情報を buff に出力します。 + * 常に指定された column の文字数となるように出力は調整されます。 + * buff_size < column の場合、出力に失敗し、false を返します。 + * + * @param buff 情報を出力するバッファ + * @param buff_size バッファサイズ + * @param entry メモリエントリ + * @param binary true の場合、データの16進数情報が出力に追加されます。 + * @param ascii true の場合、データのASCII 情報が出力に追加されます。 + * @param column 出力文字数 + * @return true/false (出力成功/出力失敗) + */ +bool kc_memory_dump(char* buff, size_t buff_size, KcMemoryEntry* entry, + int bytes, bool binary, bool ascii, int column); + + + +#ifdef __cplusplus +} // namespace kc +} // extern "C" +#endif +#endif // KC_MEMORY_DUMP_H diff --git a/modules/libkc/include/kc_overload.h b/modules/libkc/include/kc_overload.h new file mode 100644 index 0000000..5add6cd --- /dev/null +++ b/modules/libkc/include/kc_overload.h @@ -0,0 +1,79 @@ +/** + * @file kc_overload.h + * @brief オーバーロード用マクロ。 + * @copyright 2022 - 2023 Nomura Kei + * @depends none + */ +#ifndef KC_OVERLOAD_H +#define KC_OVERLOAD_H + + +/** + * 関数のオーバーロードを実現します。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) KC_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define KC_OVERLOAD(func, ...) KC_OVERLOAD_SUB(func, KC_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define KC_OVERLOAD_SUB(func, args_length) KC_STRCAT(func, args_length) + + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define KC_ARGS_LENGTH(...) KC_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define KC_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define KC_STRCAT(s1, s2) s1 ## s2 + + + +#endif // KC_OVERLOAD_H diff --git a/modules/libkc/include/kc_windows.h b/modules/libkc/include/kc_windows.h new file mode 100644 index 0000000..1c2945d --- /dev/null +++ b/modules/libkc/include/kc_windows.h @@ -0,0 +1,54 @@ +/** + * @file kc_windows.h + * @brief KC Windows 用ヘッダファイル + * @copyright 2000 - 2023 Nomura Kei + * @depends none + * + * 本ヘッダーファイルでは、Windows の場合、よく利用されるヘッダファイルを + * インクルードし、必要な設定を実施する。 + */ +#ifndef KC_WINDOWS_H +#define KC_WINDOWS_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define KC_IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#ifdef _MSV_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#else +#define KC_IS_WINDOWS (0) + +#endif + +#endif // KC_WINDOWS_H diff --git a/modules/libkc/libkc.a b/modules/libkc/libkc.a new file mode 100644 index 0000000..4c02b0b --- /dev/null +++ b/modules/libkc/libkc.a Binary files differ diff --git a/modules/libkc/obj/kc_list.d b/modules/libkc/obj/kc_list.d new file mode 100644 index 0000000..bc8d22b --- /dev/null +++ b/modules/libkc/obj/kc_list.d @@ -0,0 +1,9 @@ +obj/kc_list.o: src/kc_list.c include/kc_lock_guard.h include/kc.h \ + include/kc_windows.h include/kc_memory.h include/kc_list.h \ + include/kc_macro.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory.h: +include/kc_list.h: +include/kc_macro.h: diff --git a/modules/libkc/obj/kc_list.o b/modules/libkc/obj/kc_list.o new file mode 100644 index 0000000..b68a910 --- /dev/null +++ b/modules/libkc/obj/kc_list.o Binary files differ diff --git a/modules/libkc/obj/kc_lock_guard.d b/modules/libkc/obj/kc_lock_guard.d new file mode 100644 index 0000000..d9c1b7e --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.d @@ -0,0 +1,5 @@ +obj/kc_lock_guard.o: src/kc_lock_guard.c include/kc_lock_guard.h \ + include/kc.h include/kc_windows.h +include/kc_lock_guard.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_lock_guard.o b/modules/libkc/obj/kc_lock_guard.o new file mode 100644 index 0000000..d14a423 --- /dev/null +++ b/modules/libkc/obj/kc_lock_guard.o Binary files differ diff --git a/modules/libkc/obj/kc_memory.d b/modules/libkc/obj/kc_memory.d new file mode 100644 index 0000000..ecf0be7 --- /dev/null +++ b/modules/libkc/obj/kc_memory.d @@ -0,0 +1,7 @@ +obj/kc_memory.o: src/kc_memory.c include/kc_memory.h include/kc.h \ + include/kc_windows.h include/kc_memory_dump.h include/kc_lock_guard.h +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: +include/kc_memory_dump.h: +include/kc_lock_guard.h: diff --git a/modules/libkc/obj/kc_memory.o b/modules/libkc/obj/kc_memory.o new file mode 100644 index 0000000..34ccdc3 --- /dev/null +++ b/modules/libkc/obj/kc_memory.o Binary files differ diff --git a/modules/libkc/obj/kc_memory_dump.d b/modules/libkc/obj/kc_memory_dump.d new file mode 100644 index 0000000..2e9bd2d --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.d @@ -0,0 +1,6 @@ +obj/kc_memory_dump.o: src/kc_memory_dump.c include/kc_memory_dump.h \ + include/kc_memory.h include/kc.h include/kc_windows.h +include/kc_memory_dump.h: +include/kc_memory.h: +include/kc.h: +include/kc_windows.h: diff --git a/modules/libkc/obj/kc_memory_dump.o b/modules/libkc/obj/kc_memory_dump.o new file mode 100644 index 0000000..a6734ec --- /dev/null +++ b/modules/libkc/obj/kc_memory_dump.o Binary files differ diff --git a/modules/libkc/src/kc_list.c b/modules/libkc/src/kc_list.c new file mode 100644 index 0000000..6a5162d --- /dev/null +++ b/modules/libkc/src/kc_list.c @@ -0,0 +1,615 @@ +/** + * @file kc_list.c + * @brief リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#if defined(__GNUC__) +#define _GNU_SOURCE 1 +#define qsort_s qsort_r +#endif +#include +#include +#include +#include + +#include +#include +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// KcArrayList +// + + +/** + * KcArrayList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t element_size; //!< 要素のサイズ + int init_capacity; //!< 初期指定容量 + int capacity; //!< 現在の容量 + int size; //!< 現在の要素数 + void* data; //!< データ格納用バッファ +} KcArrayListInfo; + + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcArrayList_size(KcList* list); +static bool KcArrayList_is_empty(KcList* list); +static bool KcArrayList_contains(KcList* list, const void* element, size_t size); +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size); +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size); +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args); +static void KcArrayList_clear(KcList* list); +static void* KcArrayList_get(KcList* list, int index, size_t* size); +static bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size); +static int KcArrayList_index_of(KcList* list, const void* element, size_t size); +static int KcArrayList_last_index_of(KcList* list, const void* element, size_t size); +static KcIterator* KcArrayList_iterator(KcList* list, int index); + +static bool KcArrayList_increase_capacity(KcArrayListInfo* info); +static void KcArrayList_reduce_capacity(KcArrayListInfo* list); +static bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity); + + +/** + * 指定されたサイズの要素を扱う ArrayList を構築します。 + * + * @param size 要素のサイズ + * @param cap リストの初期容量 + */ +KcList* KcArrayList_new_ArrayList(size_t size, int cap) +{ + // KcArrayList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | element_size | + // | capacity | +------------+ + // | data -------------->| | + // +--------------+ | element[0] | + // | : | + // +------------+ + KcList* list = (KcList*) malloc(sizeof(KcList) + sizeof(KcArrayListInfo)); + void* data = malloc(size * cap); + + if ((list != NULL) && (data != NULL)) + { + list->size = KcArrayList_size; + list->is_empty = KcArrayList_is_empty; + list->contains = KcArrayList_contains; + list->add = KcArrayList_add; + list->remove = KcArrayList_remove; + list->sort = KcArrayList_sort; + list->clear = KcArrayList_clear; + list->get = KcArrayList_get; + list->set = KcArrayList_set; + list->index_of = KcArrayList_index_of; + list->last_index_of = KcArrayList_last_index_of; + list->iterator = KcArrayList_iterator; + list->_info = (list + 1); + + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->element_size = size; + info->init_capacity = cap; + info->capacity = cap; + info->size = 0; + info->data = data; + } + else + { // 何れかのメモリ確保に失敗したら、メモリを解放する。 + free(list); + list = NULL; + free(data); + data = NULL; + } + return list; +} + + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcArrayList_size(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcArrayList_is_empty(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcArrayList_contains(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + is_contains = true; + break; + } + } + + } + return is_contains; +} + + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcArrayList_add(KcList* list, int index, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index <= info->size)); + is_success = is_success && KcArrayList_increase_capacity(info); + if (is_success) + { + if (index < info->size) + { // index 以降の要素を右に移動 + size_t n = (info->size - index) * info->element_size; + memmove(&info_data[index + 1], &info_data[index], n); + } + + // データを追加 + memcpy(&info_data[index], element, info->element_size); + info->size++; + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element が NULL でない場合、削除された要素のコピーが格納されます。 + * size が NULL でない場合、削除された要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size 削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcArrayList_remove(KcList* list, int index, void* element, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (element != NULL) + { + memcpy(element, &info_data[index], info->element_size); + } + if (size != NULL) + { + *size = info->element_size; + } + + if (index != (info->size - 1)) + { // index 以降の要素を左に移動 + size_t n = (info->size - (index + 1)) * info->element_size; + memmove(&info_data[index], &info_data[index + 1], n); + } + info->size--; + } + + // 容量削減 + KcArrayList_reduce_capacity(info); + } + return is_success; +} + + + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- +/** + * [内部利用] + * ソート情報 + */ +typedef struct +{ + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args); + size_t element_size; + void* user_args; +} KcListSortInfo; + + +/** + * [内部利用] + * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。 + * + * @param x 比較する要素1 + * @param y 比較する要素2 + * @param context コンテキスト(KcListSortInfo) + * @return 比較結果 + */ +static int KcArrayList_comparator(const void* x, const void* y, void* context) +{ + KcListSortInfo* sort_info = (KcListSortInfo*) context; + int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args); + return ret; +} + + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcArrayList_sort(KcList* list, + int (*comparator)(const void* element1, size_t size1, + const void* element2, size_t size2, void* args), void* args) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + kc_lock_guard(&(info->mutex)) + { + KcListSortInfo sort_info; + sort_info.comparator = comparator; + sort_info.element_size = info->element_size; + sort_info.user_args = args; + + qsort_s( + info_data, + info->size, + info->element_size, + KcArrayList_comparator, + &sort_info); + } +} + + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcArrayList_clear(KcList* list) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + + kc_lock_guard(&(info->mutex)) + { + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + info->size = 0; + + KcArrayList_set_capacity(info, info->init_capacity); + } +} + + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +void* KcArrayList_get(KcList* list, int index, size_t* size) +{ + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + element_type* res = NULL; + kc_lock_guard(&(info->mutex)) + { + if ((0 <= index) && (index < info->size)) + { + res = &info_data[index]; + if (size != NULL) + { + *size = info->element_size; + } + } + } + return res; +} + + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。 + * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size 指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +bool KcArrayList_set(KcList* list, int index, const void* element, size_t size, + void* org_element, size_t* org_size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + is_success = ((0 <= index) && (index < info->size)); + if (is_success) + { + if (org_element != NULL) + { + memcpy(org_element, &info_data[index], info->element_size); + } + if (org_size != NULL) + { + *org_size = info->element_size; + } + memcpy(&info_data[index], element, info->element_size); + } + } + return is_success; +} + + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +int KcArrayList_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = 0; idx < info->size; idx++) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +int KcArrayList_last_index_of(KcList* list, const void* element, size_t size) +{ + UNUSED_VARIABLE(size); + KcArrayListInfo* info = (KcArrayListInfo*) list->_info; + typedef uint8_t element_type[info->element_size]; + element_type* info_data = (element_type*) info->data; + + int res; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + for (int idx = (info->size - 1); idx >= 0; idx--) + { + res = memcmp(&info_data[idx], element, info->element_size); + if (res == 0) + { + result_index = idx; + break; + } + } + + } + return result_index; +} + + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +KcIterator* KcArrayList_iterator(KcList* list, int index) +{ + (void) list; + (void) index; + return NULL; +} + + +/** + * 指定されたリスト情報のデータ容量を増やします。 + * 容量を増やす必要がない場合、何もせず true を返します。 + * 容量を増やすことができない場合、false を返します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_increase_capacity(KcArrayListInfo* info) +{ + bool is_success = true; + if (info->size >= info->capacity) + { + int new_capacity = info->capacity * 2; + is_success = KcArrayList_set_capacity(info, new_capacity); + } + return is_success; +} + + +/** + * 指定されたリスト情報のデータ容量を削減します。 + * + * @param info リスト情報 + */ +void KcArrayList_reduce_capacity(KcArrayListInfo* info) +{ + if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4))) + { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。 + int new_capacity = info->capacity / 2; + KcArrayList_set_capacity(info, new_capacity); + } +} + + +/** + * 指定されたリスト情報のデータ容量を指定された capacity に変更します。 + * + * @param info リスト情報 + * @return true/false (成功/失敗) + */ +bool KcArrayList_set_capacity(KcArrayListInfo* info, int capacity) +{ +printf("p = %p\n", info->data); +printf("size = %ld\n", info->element_size * capacity); + void* ptr = realloc(info->data, (info->element_size * capacity)); + if (ptr != NULL) + { + info->data = ptr; + info->capacity = capacity; + return true; + } + return false; +} + diff --git a/modules/libkc/src/kc_lock_guard.c b/modules/libkc/src/kc_lock_guard.c new file mode 100644 index 0000000..6ef68e5 --- /dev/null +++ b/modules/libkc/src/kc_lock_guard.c @@ -0,0 +1,50 @@ +/** + * @file kc_lock_guard.c + * @brief ロックガードモジュール + */ +#include +#include + +#include + + +/** + * 指定された mutex がロックされるまで現在のスレッドをロックします。 + * ロックに成功するとロック管理用のオブジェクトを返します。 + * + * @param mutex mutex + * @return ロック管理用オブジェクト + */ +KcLockGuard kc_lock_guard_init(mtx_t* mutex) +{ + KcLockGuard guard = { .mutex = mutex }; + if (mutex == NULL) + { + errno = EINVAL; + perror("kc_lock_guard_init: (mutex = NULL)"); + return guard; + } + + int ret = mtx_lock(mutex); + if (ret != thrd_success) + { + perror("kc_lock_guard : mtx_lock error"); + guard.mutex = NULL; + } + return guard; +} + + +/** + * 指定されたロック管理用オブジェクトのロックを解除します。 + * + * @param guard ロック管理用オブジェクト + */ +void kc_lock_guard_release(KcLockGuard* guard) +{ + if (guard->mutex != NULL) { + mtx_unlock(guard->mutex); + guard->mutex = NULL; + } +} + diff --git a/modules/libkc/src/kc_memory.c b/modules/libkc/src/kc_memory.c index 482ba35..33365d0 100644 --- a/modules/libkc/src/kc_memory.c +++ b/modules/libkc/src/kc_memory.c @@ -1,14 +1,13 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール -// @copyright 2003 - 2023 Nomura Kei -// - +/** + * @file kc_memory.c + * @brief メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #include -#include +#include #include #include -#include + // 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。 @@ -16,132 +15,491 @@ #undef KC_MEMORY_ENABLED #endif #include +#include +#include //////////////////////////////////////////////////////////////////////////////// // // 定数定義 // + /** パディング */ #define KC_MEMORY_PADDING (sizeof(void*) * 2) -//////////////////////////////////////////////////////////////////////////////// -// -// 内部変数 -// -static KcMemoryHandler kc_memory_ahandler = NULL; // 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 メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_malloc(size_t size, const char* file, const char* func, int line) +static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line) { - void* ptr = kc_memory_allocate(size, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line); return ptr; } /** - * 指定されたサイズ要素が nmemb 個からなるメモリを確保します。 + * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ + * @param alignemnt アライメント + * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_calloc (size_t nmemb, 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) +{ + 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_allocate(n, KC_MEMORY_ALLOCATED, file, func, line); + void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line); if (ptr != NULL) { memset(ptr, 0x00, n); @@ -151,19 +509,19 @@ /** - * ポインタが示すメモリブロックのサイズを size バイトに変更します。 + * 指定されたポインタが指すメモリサイズを変更します。 * - * @param ptr ポインタ + * @param ptr メモリサイズを変更するポインタ * @param size 変更後のメモリサイズ * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 + * @psram func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -void* kc_memory_realloc(void* ptr, 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) { - void* nptr = kc_memory_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); - return nptr; + void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line); + return new_ptr; } @@ -172,727 +530,352 @@ * * @param ptr 解放するメモリへのポインタ */ -void kc_memory_free(void* ptr) -{ - kc_memory_deallocate(ptr); +static void KcMemoryManager_free(void* ptr) +{ // malloc, calloc 等で確保されたメモリを解放する。 + kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED); } -// entries + +// ------------------------------------- +// _init (初回目呼出し) +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler の中では、メモリエントリの情報を操作しないでください。 - * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * [内部利用関数] + * KcMemoryManager を初期化します。 + * 内部で利用する mutex を初期化します。 */ -bool kc_memory_entries(KcMemoryHandler handler) -{ - kc_memory_init_entry(); +static void KcMemoryManager_init(void) +{ // _init に初期化ダミー関数を設定し、 + // 2回目以降本関数が Call されないようにする。 + kc_memory_manager->_init = KcMemoryManager_init_nop; - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_entries_handler, entry, NULL); - return is_executed; + // 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回目呼び出し以降) +// ------------------------------------- /** - * kc_memory_entries で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) + * KcMemoryManager の初期化ダミー関数。 + * _init の2回目以降の実行は、本関数が Call されます。 */ -static -bool kc_memory_entries_handler(KcMemoryEntry* entry, const char* msg) +static void KcMemoryManager_init_nop(void) { - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + // NOP +} + + +// ------------------------------------- +// _add +// ------------------------------------- +/** + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理に追加します。 + * + * @param entry 追加するメモリエントリ + * @return true/false (追加実施/追加失敗) + */ +static bool KcMemoryManager_add(KcMemoryEntry* entry) +{ + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - handler(next_entry, msg); - next_entry = next_entry->_next; + // [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; } -// freeif +// ------------------------------------- +// _remove +// ------------------------------------- /** - * 指定された handler に現在管理しているメモリエントリが順次渡されます。 - * handler にて、true を返したメモリが解放されます。 + * [内部利用関数] + * 指定されたメモリエントリをメモリ管理より削除します。 * - * @param handler ハンドラ - * @return true/false (処理成功/処理失敗[ロック獲得失敗]) + * @param entry 削除するメモリエントリ + * @return true/false (削除実施/削除失敗) */ -bool kc_memory_freeif(KcMemoryHandler handler) +static bool KcMemoryManager_remove(KcMemoryEntry* entry) { - kc_memory_init_entry(); - - // KcMemoryEntry のデータに handler を設定 - size_t size = sizeof(KcMemoryHandler); - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - entry->data = handler; - - bool is_executed = kc_memory_locked_execute(kc_memory_freeif_handler, entry, NULL); - return is_executed; -} - - -/** - * kc_memory_freeif で使用されるハンドラ。 - * - * @param entry エントリ(使用しない) - * @param msg メッセージ(使用しない) - * @return true(固定) - */ -static -bool kc_memory_freeif_handler(KcMemoryEntry* entry, const char* msg) -{ - KcMemoryHandler handler = (KcMemoryHandler) entry->data; - KcMemoryEntry* next_entry = kc_memory_head._next; - while (next_entry != &kc_memory_tail) + kc_memory_manager->_init(); + kc_lock_guard(kc_memory_manager->_mutex) { - bool exec_free = handler(next_entry, msg); - next_entry = next_entry->_next; - if (exec_free) - { // メモリ解放 - kc_memory_free(next_entry->_prev->data); - } + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; } return true; } +// ------------------------------------- +// _allocate +// ------------------------------------- /** - * 現在管理しているメモリ情報をダンプします。 - */ -void kc_memory_dump(void) -{ - (void) kc_memory_entries(kc_memory_dump_entry); -} - - -/** - * 指定されたメモリエントリの情報をダンプ出力します。 - * - * @param entry 出力するエントリ - * @param msg メッセージ - * @return true(固定) - */ -static -bool kc_memory_dump_entry(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - printf("%-15s:%05d:%-15s (%5d) %s ", - entry->file, - entry->line, - entry->func, - entry->size, - kc_memory_strmark(entry->_mark)); - - // dump - kc_memory_dump_data(entry, KC_MEMORY_DUMP_SIZE); - - // ascii - printf(" | "); - kc_memory_dump_data_ascii(entry, KC_MEMORY_DUMP_SIZE); - - printf("\n"); - - return true; -} - - - - -//////////////////////////////////////////////////////////////////////////////// -// -// 内部関数群 -// - - -// ============================================================================= -// メモリ確保解放 -// ============================================================================= - - -/** + * [内部利用関数] * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、NULL を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_allocate(size_t size, KcMemoryMark mark, const char* file, const char* func, int line) +static void* KcMemoryManager_allocate(size_t alignment, size_t size, + KcMemoryMark mark, const char* file, const char* func, int line) { - KcMemoryEntry* entry = (KcMemoryEntry*) malloc(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry == NULL) - { // メモリ確保失敗 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't allocate"); - return NULL; + 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; } - - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (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 が NULL の場合、kc_memory_allocate を呼び出します。 + * [内部利用関数] + * 指定された ptr のメモリサイズを変更します。 + * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。 + * 確保に失敗した場合、NULL を返します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate(void* ptr, 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) { if (ptr == NULL) { - return kc_memory_allocate(size, mark, file, func, line); + return kc_memory_manager->_allocate(0, size, mark, file, func, line); } - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; - switch (old_entry->_mark) + 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 - return kc_memory_reallocate_managed_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return kc_memory_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate と同様とする - return kc_memory_allocate(size, mark, file, func, line); + 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 - return kc_memory_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); + 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 ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_managed_ptr( - 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) { - UNUSED_VARIABLE(ptr); + KcMemoryEntry* entry = (KcMemoryEntry*) ptr; + entry--; - KcMemoryEntry* old_entry = (KcMemoryEntry*) ptr; - old_entry--; + // (A) 一旦メモリを管理から外す。 + kc_memory_manager->_remove(entry); - kc_memory_remove_entry(old_entry); - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(old_entry, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) + void* data_ptr = NULL; + KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); + if (new_entry != NULL) { // メモリ確保成功 - // メモリ管理リストに加えてポインタを返す。 - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); + 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 - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); + { // メモリ確保失敗 => エラー通知する。 + KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); + kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate"); - // 古いメモリ領域は残っているため、管理対象に戻す。 - kc_memory_add_entry(old_entry); - return NULL; + // (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。 + kc_memory_manager->_add(entry); } + return data_ptr; } +// ------------------------------------- +// _reallocate_invalid_ptr +// ------------------------------------- /** - * 管理外メモリ領域に対する realloc を実施します。 + * [内部利用関数] + * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) + * @param ptr ポインタ * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ -static -void* kc_memory_reallocate_unmanaged_ptr( - void* ptr, size_t size, KcMemoryMark mark, const char* file, const char* func, int line) -{ - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - KcMemoryEntry* entry = (KcMemoryEntry*) realloc(ptr, size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING); - if (entry != NULL) - { // メモリ確保成功 - // memmove で元々の領域+追加分を、管理領域分を確保した先にコピー - // メモリ管理リストに加えてポインタを返す。 - memmove((entry + 1), entry, size); - kc_memory_set_entry(entry, size, mark, file, func, line); - kc_memory_add_entry(entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行し、NULL を返す。 - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate"); - return NULL; - } -} - - -/** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するメモリへのポインタ (使用しない) - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static -void* kc_memory_reallocate_invalid_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) { 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; - kc_memory_set_entry(&kc_memory_error, size, mark, file, func, line); - kc_memory_ehandler(&kc_memory_error, "kc memory : can't reallocate (invalid pointer)"); return NULL; } +// ------------------------------------- +// _reallocate_unmanaged_ptr +// ------------------------------------- /** - * 指定されたポインタの指すメモリ領域を解放します。 - * NULL が指定された場合なにもしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、free を実行します。 + * [内部利用関数] + * 管理外メモリ領域に対する realloc を実施します。 * - * @param ptr 解放するメモリへのポインタ + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ */ -static -void kc_memory_deallocate(void* ptr) +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) - { // NULL ポインタに対してはなにもしない + { return; } KcMemoryEntry* entry = (KcMemoryEntry*) ptr; entry--; - switch (entry->_mark) - { - case KC_MEMORY_ALLOCATED: // 管理メモリ - kc_memory_deallocate_entry(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete"); - kc_memory_execute_ehandler(entry, "warning : please use delete"); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - kc_memory_deallocate_entry(entry); - perror("kc memory : warning : please use delete[]"); - kc_memory_execute_ehandler(entry, "warning : please use delete[]"); - break; - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - default: - free(ptr); - break; - } -} - - -/** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ -static -void kc_memory_deallocate_entry(KcMemoryEntry* entry) -{ - (void) kc_memory_remove_entry(entry); - entry->_mark = KC_MEMORY_DELETED; - entry->size = 0; - free(entry); -} - - -// ============================================================================= -// メモリエントリ管理 -// ============================================================================= - - -/** - * メモリ管理エントリ全体を初期化します。 - */ -static -void kc_memory_init_entry(void) -{ - static bool kc_memory_entry_initialized = false; - if (!kc_memory_entry_initialized) - { - // メモリ管理エントリ初期化 - kc_memory_set_entry(&kc_memory_head , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_tail , 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_set_entry(&kc_memory_error, 0, KC_MEMORY_DELETED, NULL, NULL, 0); - kc_memory_head._prev = kc_memory_head._next = &kc_memory_tail; - kc_memory_tail._prev = kc_memory_tail._next = &kc_memory_head; - - kc_memory_entry_initialized = true; - } -} - - -/** - * 指定された entry を指定された各パラメータで初期化します。 - * - * @param entry 初期化するエントリ - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報を示すマーク - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ -static -void kc_memory_set_entry(KcMemoryEntry* entry, size_t size, KcMemoryMark 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->data = (entry + 1); - entry->_prev = NULL; - entry->_next = NULL; -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * メモリがリストに追加された際、予め登録されたメモリ確保のハンドラが実行されます。 - * - * @param entry 追加するエントリ - */ -static -void kc_memory_add_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_add_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_ahandler(entry, "allocate memory"); + if (entry->mark == expected_mark) + { // 期待するメモリ状態の場合、そのまま解放する。 + kc_memory_manager->_listener.free(entry); + kc_memory_manager->_remove(entry); + KcMemoryEntry_delete(entry); } else - { - perror("kc memory : can't add entry"); - kc_memory_execute_ehandler(entry, "can't add entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストに追加します。 - * - * @param entry 追加するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_add_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - // [tail] の一つ前に挿入する。 - entry->_next = &kc_memory_tail; - entry->_prev = kc_memory_tail._prev; - kc_memory_tail._prev->_next = entry; - kc_memory_tail._prev = entry; - return true; -} - - -/** - * 指定されたエントリをメモリ管理のリストから削除します。 - * メモリがリストに追加された際、予め登録されたメモリ解放のハンドラが実行されます。 - * - * @param entry 削除するエントリ - */ -static -void kc_memory_remove_entry(KcMemoryEntry* entry) -{ - kc_memory_init_entry(); - bool is_executed = kc_memory_locked_execute(kc_memory_remove_entry_handler, entry, NULL); - if (is_executed) - { - kc_memory_execute_fhandler(entry, "free memory"); - } - else - { - perror("kc memory : can't remove entry"); - kc_memory_execute_ehandler(entry, "can't remove entry"); - } -} - - -/** - * 指定されたエントリをメモリ管理のリストより削除します。 - * - * @param entry 削除するエントリ - * @param msg メッセージ (使用されません) - * @return true (固定) - */ -static -bool kc_memory_remove_entry_handler(KcMemoryEntry* entry, const char* msg) -{ - UNUSED_VARIABLE(msg); - - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - return true; -} - - - -// ============================================================================= -// ハンドラ実行 -// ============================================================================= - - -/** - * メモリ確保時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 確保したメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ahandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ahandler != NULL) - { - kc_memory_ahandler(entry, msg); - } -} - - -/** - * メモリ解放時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 解放するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_fhandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_fhandler != NULL) - { - kc_memory_fhandler(entry, msg); - } -} - - -/** - * エラー発生時のハンドラを実行します。 - * ハンドラが未登録の場合何もしません。 - * - * @param entry 関連するメモリエントリ - * @param msg メッセージ - */ -static -void kc_memory_execute_ehandler(KcMemoryEntry* entry, const char* msg) -{ - if (kc_memory_ehandler != NULL) - { - kc_memory_ehandler(entry, msg); - } -} - - -// ============================================================================ -// 同期実行 -// ============================================================================= - - -/** - * 同期化実現のための mutex を初期化します。 - * - * @return true/false (初期化成功/失敗) - */ -static -bool kc_memory_mutex_init(void) -{ - static bool kc_memory_mutex_initialized = false; - if (!kc_memory_mutex_initialized) - { // 未初期化の場合のみ実施する。 - // kc_memory_mutex は、kc_memory_locked_execute 内でのみ利用する。 - // mtx_recursive を使用し、同関数内での再帰呼び出し - // (handler内でのkc_memory_locked_execute)を許容する。 - int result = mtx_init(&kc_memory_mutex, mtx_plain | mtx_recursive); - if (result == thrd_success) + { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 + switch (entry->mark) { - kc_memory_mutex_initialized = true; + 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; } - else - { - perror("kc memory : can't init mutex"); - kc_memory_execute_ehandler(NULL, "can't init mutex"); - } - } - return kc_memory_mutex_initialized; -} - - -/** - * 指定された handler の実行を同期化します。 - * mutex によるロック失敗により、handler を実行できなかった場合、false を返します。 - * - * @param handler 同期化して実行する関数 - * @param entry handler へ渡される第一引数 (操作するメモリエントリ) - * @param msg handler へ渡される第二引数 - * @return true/false (handler を実行した/handler を実行できなかった) - */ -static -bool kc_memory_locked_execute(KcMemoryHandler handler, KcMemoryEntry* entry, const char* msg) -{ - (void) kc_memory_mutex_init(); - - int is_locked = mtx_lock(&kc_memory_mutex); - if (is_locked == thrd_success) - { - // ハンドラの戻り値は Don't Care - (void) handler(entry, msg); - - bool is_unlocked = mtx_unlock(&kc_memory_mutex); - if (is_unlocked != thrd_success) - { - perror("memory : can't unlock"); - kc_memory_execute_ehandler(NULL, "can't unlock"); - } - // アンロックに失敗しても handler 自体は実行しているため true を返す。 - return true; - } - return false; -} - - - -// ============================================================================ -// データダンプ -// ============================================================================= - -/** - * 指定されたバイトを ASCII 文字に変換します。 - * - * @param c バイト - */ -#define KC_MEMORY_TO_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') - - -/** - * 指定された確保メモリ状態(mark)に対応する文字列表現を返します。 - * - * @param mark 確保メモリ状態 - * @return 確保メモリ状態に対応する文字列表現 - */ -static -const char* kc_memory_strmark(int mark) -{ - switch (mark) - { - case KC_MEMORY_DELETED: return "del "; - case KC_MEMORY_ALLOCATED: return "alloc"; - case KC_MEMORY_ALLOCATED_NEW: return "new "; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: return "new[]"; - default: return "other"; - } -} - - -/** - * 指定されたメモリエントリのデータをダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%02X ", data_ptr[idx]); - } - - for (; idx < dump_size; idx++) - { - printf("-- "); - } -} - - -/** - * 指定されたメモリエントリのデータを ASCII 形式でダンプします。 - * - * @param entry エントリ - * @param dump_size ダンプするサイズ(バイト数) - */ -static -void kc_memory_dump_data_ascii(KcMemoryEntry* entry, int dump_size) -{ - const unsigned char* data_ptr = (const unsigned char*) entry->data; - int data_len = ((int) entry->size < dump_size) ? (int) entry->size : dump_size; - int idx = 0; - - for (; idx < data_len; idx++) - { - printf("%c", KC_MEMORY_TO_ASCII(data_ptr[idx])); - } - - for (; idx < dump_size; idx++) - { - printf(" "); } } diff --git a/modules/libkc/src/kc_memory_dump.c b/modules/libkc/src/kc_memory_dump.c new file mode 100644 index 0000000..7b4460d --- /dev/null +++ b/modules/libkc/src/kc_memory_dump.c @@ -0,0 +1,290 @@ +/** + * @file kc_memory_dump.c + * @brief KC メモリダンプモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// 構造体 +// + +/** + * バッファ情報構造体 + */ +typedef struct +{ + char* write_ptr; //rest_size - 1)) + ? max_column : (info->rest_size - 1); + + info_column -= (binary) ? (bytes * 3) + 3 : 0; + info_column -= (ascii ) ? (bytes ) + 3 : 0; + + return info_column; +} + + +/** + * 指定された info の write_ptr に指定されたメッセージ msg を書き込みます。 + * info の rest_size が 0 未満の場合は、何もしません。 + * info の rest_size を超えるメッセージは書き込まれません。 + * info->rest_size または、msg のサイズが INT_MAX を超える場合の動作は保証しません。 + * + * @param info バッファ情報 + * @param msg 書き込むメッセージ + */ +static void KcMemoryDump_dump_message(KcMemoryDumpBufferInfo* info, const char* msg) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + int write_size = snprintf(info->write_ptr, info->rest_size, msg); + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + info->write_ptr += write_size; + info->rest_size -= write_size; +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの情報を書き込みます。 + * 書き込む情報の文字数は、column に指定された文字数揃えられます。 + * ※空白でパディングされます。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param column 制限文字数 + */ +static void KcMemoryDump_dump_info( + KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int column) +{ + if (info->rest_size <= 0) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + char size_buff[16]; + KcMemoryDump_format_size(size_buff, sizeof(size_buff), entry->size); + int write_size = snprintf(info->write_ptr, info->rest_size, "%s:%d (%s) [func=%s]", + entry->file, entry->line, size_buff, entry->func); + int padding = column - write_size; + if (write_size > info->rest_size) + { // msg が切り詰められ、info->rest_size - 1 分書き込んだ ('\0'除く) + write_size = (info->rest_size - 1); + } + + if (write_size > column) + { // 最大文字数より多く書き込んでいたら、最大文字数で制限をかける。 + write_size = column; + } + + info->write_ptr += write_size; + info->rest_size -= write_size; + *(info->write_ptr) = '\0'; + + if (padding > 0) + { + padding = (padding < info->rest_size) ? padding : (info->rest_size - 1); + memset(info->write_ptr, ' ', padding); + info->write_ptr += padding; + info->rest_size -= padding; + *(info->write_ptr) = '\0'; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリの16進数ダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_binary(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes * 3; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%02X ", data_ptr[idx]); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "-- "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたバッファに、指定されたメモリエントリのASCIIダンプを出力します。 + * バッファサイズが不足する場合は、何も出力しません。 + * + * @param info バッファ情報 + * @param entry メモリエントリ + * @param bytes ダンプするバイト数 + */ +static void KcMemoryDump_dump_ascii(KcMemoryDumpBufferInfo* info, KcMemoryEntry* entry, int bytes) +{ + int required_size = bytes; + if (info->rest_size < required_size) + { // 空きバッファサイズ無しのため、何もしない。 + return; + } + + const unsigned char* data_ptr = (const unsigned char*) entry->data; + int data_len = ((int) entry->size < bytes) ? (int) entry->size : bytes; + + int idx = 0; + for (; idx < data_len; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, "%c", KC_MEMORY_DUMP_TO_ASCII(data_ptr[idx])); + info->write_ptr += write_size; + info->rest_size -= write_size; + } + for (; idx < bytes; idx++) + { + int write_size = snprintf(info->write_ptr, info->rest_size, " "); + info->write_ptr += write_size; + info->rest_size -= write_size; + } +} + + +/** + * 指定されたサイズ (size) の単位付き文字列表現の文字列を指定されたバッファに格納します。 + * バッファのサイズは、12 Byte 以上である必要があります。 + * + * @param buff バッファ + * @param size サイズ + */ +static void KcMemoryDump_format_size(char* buff, size_t buff_size, size_t size) +{ + // UINT64_MAX ~ 16EB, ... PB, EB, ZB, YB, RB, QB + static const char* SIZE_UNIT[] = { " B", "KB", "MB", "GB", "TB", "PB", "EB" }; + int unit_index = 0; + double view_size = (double) size; + while (view_size >= 1024) + { + view_size /= 1024; + unit_index++; + } + snprintf(buff, buff_size, "%8.3lf %s", view_size, SIZE_UNIT[unit_index]); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index 0f0723c..77bcad3 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -1,73 +1,11 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// KCPP Header File -// +/** + * @file kcpp.h + * @brief Kantan C++ Library 共通ヘッダーファイル。 + */ #ifndef KCPP_HPP #define KCPP_HPP +#include -#if defined(__cplusplus) && (__cplusplus >= 201703L) -// ============================================================================= -// C++17 -// ============================================================================= -#include - - -#else -// ============================================================================= -// ERROR -// ============================================================================= -#error "suuports C++17 or later" - - -#endif // C++17, ERROR - - - -// ============================================================================= -// Windows 判定 & 基本設定 -// ============================================================================= -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) \ - || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define KCPP_IS_WINDOWS (1) - -// Windows の場合、よく利用するヘッダのインクルードや必要な設定を実施する。 - -// DMC にて winsock2.h を使用する場合, _WINSOCKAPI_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x0500 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// 必要なヘッダーをインクルードする -#include -#include -#include -#ifdef _MSV_VER -#pragma comment(lib, "ws2_32.lib") -#endif - -#else -#define KCPP_IS_WINDOWS (0) - -#endif #endif // KCPP_HPP diff --git a/modules/libkcpp/include/kcpp_assert.hpp b/modules/libkcpp/include/kcpp_assert.hpp index e96259a..2ff13b9 100644 --- a/modules/libkcpp/include/kcpp_assert.hpp +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -18,36 +18,240 @@ class AssertError : public Error { public: + /** + * 最後に発生したエラーメッセージを持つ AssertError を構築します。 + * エラーメッセージを取得できない場合、空文字列がメッセージに設定されます。 + */ AssertError() noexcept; + + + /** + * AssertError をコピーして生成します。 + * + * @param t コピー元 + */ AssertError(const AssertError& t) noexcept; + + + /** + * 指定されたメッセージを持つ AssertError を構築します。 + * + * @param msg メッセージ + */ AssertError(const std::string& msg) noexcept; + + + /** + * 指定されたメッセージおよび、エラー発生情報を持つ AssertError を構築します。 + * + * @param msg メッセージ + * @param file エラー発生ファイル名 + * @param func エラー発生関数名 + * @param line エラー発生行番号 + */ AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + + + /** + * AssertErro を破棄します。 + */ virtual ~AssertError() noexcept; + + + /** + * エラー発生ファイル名を返します。 + * + * @return エラー発生ファイル名 + */ const std::string& getFile() const noexcept; + + + /** + * エラー発生関数名を返します。 + * + * @return エラー発生関数名 + */ const std::string& getFunc() const noexcept; - int getLine() const noexcept; + + + /** + * エラー発生行番号を返します。 + * + * @return エラー発生行番号 + */ + int getLine() const noexcept; + private: - std::string errorFile; - std::string errorFunc; - int errorLine; + std::string errorFile; //< エラー発生ファイル名 + std::string errorFunc; //< エラー発生関数名 + int errorLine; //< エラー発生行番号 }; + namespace Assert { + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (char expected, char actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (int expected, int actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (long expected, long actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (double expected, double actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が期待する値かを判定し、期待しない値の場合、AssertError を throw します。 + * + * @param expected 期待する値 + * @param actual 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + + + /** + * 指定された値が true か否かを判定し、true でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertTrue (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が false か否かを判定し、false でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertFalse (bool condition , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr でない場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNull (void* obj , const char* file, const char* func, int line); + + + /** + * 指定された値が nullptr か否かを判定し、nullptr の場合、AssertError を throw します。 + * + * @param condition 比較する値 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ void assertNotNull(void* obj , const char* file, const char* func, int line); - void fail( const char* file, const char* func, int line); + + + /** + * 常に AssertError を throw します。 + * + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void assertFail( const char* file, const char* func, int line); + /** * 指定された actual が、expected と同一でない場合、AssertError を throw します。 @@ -88,12 +292,12 @@ * @param obj 比較する値 */ #define assertNotNull(obj) assertNotNull(obj, __FILE__, __func__, __LINE__) - - + + /** * 常に、AssertError を throw します。 */ - #define fail() fail(__FILE__, __func__, __LINE__) + #define assertFail() assertFail(__FILE__, __func__, __LINE__) } } diff --git a/modules/libkcpp/include/kcpp_dl.hpp b/modules/libkcpp/include/kcpp_dl.hpp new file mode 100644 index 0000000..84217a5 --- /dev/null +++ b/modules/libkcpp/include/kcpp_dl.hpp @@ -0,0 +1,68 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Dyanmic Loader Header File +// ライブラリの動的ロードを行うモジュール +// +#ifndef KCPP_DL_HPP +#define KCPP_DL_HPP + +#include + +#include +#include + + +namespace kcpp +{ +#if (KCPP_IS_WINDOWS) + typedef HMODULE dl_handle_t; + typedef FARPROC WINAPI dl_func_t; + +#else + typedef void* dl_handle_t; + typedef void* dl_func_t; + +#endif + + + /** + * 動的ライブラリのロードエラー発生時に呼び出される例外です。 + */ + class DynamicLoadException : public Exception + { + public: + DynamicLoadException() noexcept; + DynamicLoadException(const DynamicLoadException& t) noexcept; + DynamicLoadException(const std::string& msg) noexcept; + virtual ~DynamicLoadException() noexcept; + }; + + + /** + * 動的ライブラリのローダー。 + * 動的にライブラリをロードするクラスです。 + * + * 使用例 + * + * @code + * typedef void (*SampleFp)(void* data, size_t size); + * ...(省略)... + * DynamicLinkingLoader loader("sample.so"); + * SampleFp dumpSample = reinterpret_cast(loader.sym("dumpSample"); + * dumpSample(data, sizeof(data)); + */ + class DynamicLoader + { + public: + DynamicLoader(const std::string& name); + virtual ~DynamicLoader(); + dl_func_t sym(const std::string& symbol); + private: + DynamicLoader() noexcept = delete; + DynamicLoader(const DynamicLoader&) = delete; + DynamicLoader operator=(const DynamicLoader&) = delete; + dl_handle_t handle; + }; +} + +#endif // KCPP_DL_HPP diff --git a/modules/libkcpp/include/kcpp_memory.hpp b/modules/libkcpp/include/kcpp_memory.hpp index e7f6f38..f8ba772 100644 --- a/modules/libkcpp/include/kcpp_memory.hpp +++ b/modules/libkcpp/include/kcpp_memory.hpp @@ -1,8 +1,8 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール ヘッダファイル -// @copyright 2003 - 2023 Nomura Kei -// +/** + * @file kcpp_memory.hpp + * @brief KC++ メモリ管理モジュール + * @copyright 2003 - 2023 Nomura Kei + */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP diff --git a/modules/libkcpp/include/kcpp_unittest.hpp b/modules/libkcpp/include/kcpp_unittest.hpp index 4bf8954..b011d66 100644 --- a/modules/libkcpp/include/kcpp_unittest.hpp +++ b/modules/libkcpp/include/kcpp_unittest.hpp @@ -5,9 +5,103 @@ #ifndef KCPP_UNITTEST_HPP #define KCPP_UNITTEST_HPP +#include + +#include + + namespace kcpp { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成ください。 + * + * 以下実装例 + * + * @code + * #include + * + * using namespace kcpp; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する内容を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する内容を記載する。 + * } + * void testSample1() + * { // テスト内容を記述する。 + * std::string tmp("ABC"); + * Asssert::assertEquals("ABC", tmp); + * } + * void testSample2() + * { // テスト内容を記述する。 + * } + * void run() + * { + * RUN_TEST(testSample1, "sample1 test"); + * RUN_TEST(testSample2, "sample2 test"); + * } + * }; + * @endcode + */ + class TestCase + { + public: + TestCase(); + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void run() = 0; + }; + + + + /** + * 単体試験を管理するクラス。 + * 本クラスは、単体テストにて使用されます。 + * 通常、本クラスのインスタンスを生成する必要はありません。 + */ + class UnittestManager + { + public: + UnittestManager(); + virtual ~UnittestManager(); + void addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e = nullptr) noexcept; + void printResult(); + private: + int okCount; + int ngCount; + }; + + + // UnittestManager + extern UnittestManager utManager; + + + #define RUN_TEST(func, msg) { \ + setUp(); \ + try \ + { \ + func(); \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, true); \ + } \ + catch (kcpp::AssertError& e) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + kcpp::utManager.addResult(msg, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } } diff --git a/modules/libkcpp/libkcpp.a b/modules/libkcpp/libkcpp.a new file mode 100644 index 0000000..89f7afd --- /dev/null +++ b/modules/libkcpp/libkcpp.a Binary files differ diff --git a/modules/libkcpp/obj/kcpp_assert.d b/modules/libkcpp/obj/kcpp_assert.d new file mode 100644 index 0000000..3c931fb --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.d @@ -0,0 +1,9 @@ +obj/kcpp_assert.o: src/kcpp_assert.cpp include/kcpp_assert.hpp \ + include/kcpp_error.hpp include/kcpp_throwable.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_assert.o b/modules/libkcpp/obj/kcpp_assert.o new file mode 100644 index 0000000..a1f178d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_assert.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_dl.d b/modules/libkcpp/obj/kcpp_dl.d new file mode 100644 index 0000000..7d78c48 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.d @@ -0,0 +1,9 @@ +obj/kcpp_dl.o: src/kcpp_dl.cpp include/kcpp_dl.hpp include/kcpp.hpp \ + ../../include/kc.h ../../include/kc_windows.h include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp +include/kcpp_dl.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: diff --git a/modules/libkcpp/obj/kcpp_dl.o b/modules/libkcpp/obj/kcpp_dl.o new file mode 100644 index 0000000..c4521ba --- /dev/null +++ b/modules/libkcpp/obj/kcpp_dl.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_error.d b/modules/libkcpp/obj/kcpp_error.d new file mode 100644 index 0000000..9b556a8 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.d @@ -0,0 +1,8 @@ +obj/kcpp_error.o: src/kcpp_error.cpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_error.o b/modules/libkcpp/obj/kcpp_error.o new file mode 100644 index 0000000..313af89 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_error.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_exception.d b/modules/libkcpp/obj/kcpp_exception.d new file mode 100644 index 0000000..baba0de --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.d @@ -0,0 +1,8 @@ +obj/kcpp_exception.o: src/kcpp_exception.cpp include/kcpp_exception.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_exception.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_exception.o b/modules/libkcpp/obj/kcpp_exception.o new file mode 100644 index 0000000..3d68424 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_exception.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_memory.d b/modules/libkcpp/obj/kcpp_memory.d new file mode 100644 index 0000000..7635aa9 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.d @@ -0,0 +1,6 @@ +obj/kcpp_memory.o: src/kcpp_memory.cpp include/kcpp_memory.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_memory.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_memory.o b/modules/libkcpp/obj/kcpp_memory.o new file mode 100644 index 0000000..71b2fb0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_memory.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_throwable.d b/modules/libkcpp/obj/kcpp_throwable.d new file mode 100644 index 0000000..b8ba76d --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.d @@ -0,0 +1,6 @@ +obj/kcpp_throwable.o: src/kcpp_throwable.cpp include/kcpp_throwable.hpp \ + include/kcpp.hpp ../../include/kc.h ../../include/kc_windows.h +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_throwable.o b/modules/libkcpp/obj/kcpp_throwable.o new file mode 100644 index 0000000..c320f21 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_throwable.o Binary files differ diff --git a/modules/libkcpp/obj/kcpp_unittest.d b/modules/libkcpp/obj/kcpp_unittest.d new file mode 100644 index 0000000..b328116 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.d @@ -0,0 +1,11 @@ +obj/kcpp_unittest.o: src/kcpp_unittest.cpp include/kcpp_unittest.hpp \ + include/kcpp_assert.hpp include/kcpp_error.hpp \ + include/kcpp_throwable.hpp include/kcpp.hpp ../../include/kc.h \ + ../../include/kc_windows.h +include/kcpp_unittest.hpp: +include/kcpp_assert.hpp: +include/kcpp_error.hpp: +include/kcpp_throwable.hpp: +include/kcpp.hpp: +../../include/kc.h: +../../include/kc_windows.h: diff --git a/modules/libkcpp/obj/kcpp_unittest.o b/modules/libkcpp/obj/kcpp_unittest.o new file mode 100644 index 0000000..092a8e0 --- /dev/null +++ b/modules/libkcpp/obj/kcpp_unittest.o Binary files differ diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp index cfd47c2..81ca1bb 100644 --- a/modules/libkcpp/src/kcpp_assert.cpp +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -17,89 +17,42 @@ namespace kcpp { - /** - * 最後に発生したエラーメッセージを持つ AssertError を構築します。 - * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 - */ AssertError::AssertError() noexcept : Error(), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * コピーコンストラクタ。 - * - * @param t コピー元 - */ AssertError::AssertError(const AssertError& t) noexcept : Error(t), errorFile(t.errorFile), errorFunc(t.errorFunc), errorLine(t.errorLine) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - */ AssertError::AssertError(const std::string& msg) noexcept : Error(msg), errorFile(""), errorFunc(""), errorLine(0) { // NOP } - - /** - * 指定されたメッセージを持つ AssertError を構築します。 - * - * @param msg メッセージ - * @param file エラー発生ファイル名 - * @param func エラー発生関数名 - * @param line エラー発生行番号 - */ AssertError::AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept : Error(msg), errorFile(file), errorFunc(func), errorLine(line) { // NOP } - - /** - * デストラクタ。 - */ AssertError::~AssertError() noexcept { // NOP } - - /** - * エラー発生ファイル名を返します。 - * - * @return エラー発生ファイル名 - */ const std::string& AssertError::getFile() const noexcept { return errorFile; } - - /** - * エラー発生関数名を返します。 - * - * @return エラー発生関数名 - */ const std::string& AssertError::getFunc() const noexcept { return errorFunc; } - - /** - * エラー発生行番号を返します。 - * - * @return エラー発生行番号 - */ int AssertError::getLine() const noexcept { return errorLine; @@ -108,17 +61,6 @@ namespace Assert { - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(bool expected, bool actual, const char* file, const char* func, int line) { if (expected != actual) @@ -130,18 +72,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(char expected, char actual, const char* file, const char* func, int line) { if (expected != actual) @@ -152,18 +82,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(int expected, int actual, const char* file, const char* func, int line) { if (expected != actual) @@ -174,18 +92,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(long expected, long actual, const char* file, const char* func, int line) { if (expected != actual) @@ -196,18 +102,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(double expected, double actual, const char* file, const char* func, int line) { if (expected != actual) @@ -218,18 +112,6 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const std::string& actual, const char* file, const char* func, int line) { if (expected != actual) @@ -240,54 +122,18 @@ } } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const std::string& actual, const char* file, const char* func, int line) { std::string expectedStr = expected; assertEquals(expectedStr, actual, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const std::string& expected, const char* actual, const char* file, const char* func, int line) { std::string actualStr = actual; assertEquals(expected, actualStr, file, func, line); } - - /** - * 値を比較します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param expected 期待する値 - * @param actual 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertEquals(const char* expected, const char* actual, const char* file, const char* func, int line) { std::string expectedStr = expected; @@ -295,49 +141,16 @@ assertEquals(expectedStr, actualStr, file, func, line); } - - /** - * 指定された condition が、true でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertTrue(bool condition, const char* file, const char* func, int line) { assertEquals(true, condition, file, func, line); } - - /** - * 指定された condition が、false でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertFalse(bool condition, const char* file, const char* func, int line) { assertEquals(false, condition, file, func, line); } - - /** - * 指定された obj が、nullptr でない場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNull(void* obj, const char* file, const char* func, int line) { if (obj != nullptr) @@ -346,17 +159,6 @@ } } - - /** - * 指定された obj が、nullptr の場合、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void assertNotNull(void* obj, const char* file, const char* func, int line) { if (obj == nullptr) @@ -365,17 +167,6 @@ } } - - /** - * 常に、AssertError を throw します。 - * 通常、本メソッドを直接使用せず、assertEquals(expected, actual) マクロを使用してください。 - * 指定された expected と actual が不一致の場合、AssertError を throw します。 - * - * @param condition 比較する値 - * @param file ファイル名 - * @param func 関数名 - * @param line 行番号 - */ void fail(const char* file, const char* func, int line) { throw AssertError("fail()", file, func, line); diff --git a/modules/libkcpp/src/kcpp_dl.cpp b/modules/libkcpp/src/kcpp_dl.cpp new file mode 100644 index 0000000..bffd3fb --- /dev/null +++ b/modules/libkcpp/src/kcpp_dl.cpp @@ -0,0 +1,128 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// ライブラリの動的ロードを行うモジュール +// + +#include + +#if (!KCPP_IS_WINDOWS) +#include +#endif + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoadException + // + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + DynamicLoadException::DynamicLoadException() noexcept : Exception() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + DynamicLoadException::DynamicLoadException(const DynamicLoadException& t) noexcept : Exception(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + DynamicLoadException::DynamicLoadException(const std::string& msg) noexcept : Exception(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + DynamicLoadException::~DynamicLoadException() noexcept + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // DynamicLoader + // + + /** + * 指定された動的ライブラリをロードします。 + * ロードに失敗した場合、DynamicLoadException を throw します。 + * + * @param name ロードする動的ライブラリ + */ + DynamicLoader::DynamicLoader(const std::string& name) : handle(nullptr) + { +#if (KCPP_IS_WINDOWS) + handle = ::LoadLibraryEx(name.c_str(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); +#else + handle = ::dlopen(name.c_str(), RTLD_LAZY); +#endif + if (handle == nullptr) + { + throw DynamicLoadException("can't load file"); + } + } + + + /** + * 指定された動的ライブラリをアンロードします。 + */ + DynamicLoader::~DynamicLoader() + { +#if (KCPP_IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + + /** + * 指定されたシンボルがロードされたアドレスを返します。 + * 該当シンボルが見つからない場合、DynamicLoadException を throw します。 + * + * @param name シンボル名 + */ + dl_func_t DynamicLoader::sym(const std::string& name) + { + dl_func_t func; +#if (KCPP_IS_WINDOWS) + func = ::GetProcAddress(handle, name.c_str()); + if (func == nullptr) + { + throw DynamicLoadException("no such symbol"); + } +#else + (void) dlerror(); + func = ::dlsym(handle, name.c_str()); + char* errmsg = dlerror(); + if (errmsg != nullptr) + { + throw DynamicLoadException(errmsg); + } +#endif + return func; + } + +} diff --git a/modules/libkcpp/src/kcpp_unittest.cpp b/modules/libkcpp/src/kcpp_unittest.cpp new file mode 100644 index 0000000..beb08ab --- /dev/null +++ b/modules/libkcpp/src/kcpp_unittest.cpp @@ -0,0 +1,142 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Unit Test Module +// + +#include +#include +#include +#include + +#include + + +namespace kcpp +{ + + //////////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /* + * テストケースクラスを構築します。 + */ + TestCase::TestCase() + { + // NOP + } + + + /* + * テストケースクラスを破棄します。 + */ + TestCase::~TestCase() + { + // NOP + } + + + /** + * 各テストケース実行前に実行されます。 + */ + void TestCase::setUp() + { + // NOP + } + + + /** + * 各テストケース実行後に実行されます。 + */ + void TestCase::tearDown() + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // UnittestManager + // + + /** + * テスト管理クラスを構築します。 + */ + UnittestManager::UnittestManager() : okCount(0), ngCount(0) + { + // NOP + } + + + /** + * テスト管理クラスを破棄します。 + */ + UnittestManager::~UnittestManager() + { + // NOP + } + + + /** + * テスト結果を出力します。 + * + * @param msg メッセージ + * @param funcName テスト関数名 + * @param file テスト実行呼び出し元ファイル名 + * @param line テスト実行呼び出し元行番号 + * @param result テスト結果 + * @param e エラー情報 + */ + void UnittestManager::addResult(const char* msg, const char* funcName, + const char* file, int line, bool result, const AssertError* e) noexcept + { + std::ostringstream testInfo; + testInfo << file << ":" << line << " (" << funcName << ") " << msg; + std::cout << "[" + << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << "] " + << std::setw(64) << std::setfill(' ') << std::left << testInfo.str(); + if (result) + { + std::cout << " [ OK ]" << std::endl; + okCount++; + } + else + { + std::cout << " [ NG ]" << std::endl; + if (e != nullptr) + { + std::cout << "AssertError:" << e->what() << std::endl; + std::cout << " at " << e->getFile() + << ":" << e->getLine() + << " (" << e->getFunc() << ")" << std::endl; + } + ngCount++; + } + } + + + /** + * テスト結果まとめを出力します。 + */ + void UnittestManager::printResult() + { + std::cout << std::endl; + std::cout << "----------------" << std::endl; + std::cout << " OK : " << std::setw(5) << std::right << okCount << std::endl; + std::cout << " NG : " << std::setw(5) << std::right << ngCount << std::endl; + std::cout << " Total : " << std::setw(5) << std::right << (okCount + ngCount) << std::endl; + std::cout << "----------------" << std::endl; + std::cout << std::endl; + + } + + + + /** + * UnittestManager のインスタンス。 + */ + UnittestManager utManager; +} + diff --git a/modules/libut/Makefile b/modules/libut/Makefile new file mode 100644 index 0000000..8a633d3 --- /dev/null +++ b/modules/libut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libut +TARGET = $(NAME).a +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/libut/include/ut.h b/modules/libut/include/ut.h new file mode 100644 index 0000000..ab61994 --- /dev/null +++ b/modules/libut/include/ut.h @@ -0,0 +1,80 @@ +/** + * @file ut.h + * @brief Unittest For C + * @copyright 2023 Nomura Kei + */ +#ifndef UT_H +#define UT_H + + +//////////////////////////////////////////////////////////////////////////////// +// +// C/C++ Version チェック +// + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +// C11 以降 +#include +#include + +#elif defined(__cplusplus) && (__cplusplus >= 201703L) +// C++17 以降対応 +#include + +#else +// 非対応 +#error "suuports C11, C++17 or later" + +#endif + +#include + + + +//////////////////////////////////////////////////////////////////////////////// +// +// UT +// + + +/** + * Ut 用構造体 + */ +typedef struct +{ + + /** + * テストケースを追加します。 + * + * @param func_name テストメッセージ + * @param test_func 追加するテストケース + */ + void (*add)(const char* msg, void (*test_func)(void)); + + + /** + * テストケースを実行します。 + */ + void (*run)(void); + +} Ut; + + +/** + * Ut を構築します。 + * + * @return Ut + */ +Ut* Ut_new(void); + + +/** + * Ut を破棄します。 + * + * @param ut 破棄する Ut + */ +void Ut_delete(Ut* ut); + + + +#endif // UT_H diff --git a/modules/libut/include/ut_assert.h b/modules/libut/include/ut_assert.h new file mode 100644 index 0000000..6847cb4 --- /dev/null +++ b/modules/libut/include/ut_assert.h @@ -0,0 +1,57 @@ +/** + * @file ut_asserrt.h + * @brief テスト用アサーション + * @copyright 2023 Nomura Kei + */ +#ifndef UT_ASSERT_H +#define UT_ASSERT_H + + +#include + +// #define add(...) UT_OVERLOAD(add_, __VA_ARGS__) +#define UT_OVERLOAD(func, ...) UT_OVERLOAD_SUB(func, UT_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) +#define UT_OVERLOAD_SUB(func, args_length) UT_STRCAT(func, args_length) +#define UT_ARGS_LENGTH(...) UT_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define UT_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH +#define UT_STRCAT(s1, s2) s1 ## s2 + + +#define _ut_assert_equals_2(expected, actual) \ + _Generic((expected), \ + int : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + long : _Generic((actual), \ + int : ut_assert_equals_l_l_2, \ + long : ut_assert_equals_l_l_2, \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2 \ + ), \ + float : ut_assert_equals_d_d_2, \ + double : ut_assert_equals_d_d_2, \ + char* : ut_assert_equals_s_s_2 \ + )(expected, actual) + + +#define Ut_assert_null(condition) ut_assert_null(object, __FILE__, __func__, __LINE__) +#define Ut_assert_not_null(condition) ut_assert_false(object, __FILE__, __func__, __LINE__) +#define Ut_assert_false(condition) ut_assert_false(condition, __FILE__, __func__, __LINE__) +#define Ut_assert_true(condition) ut_assert_true( condition, __FILE__, __func__, __LINE__) +#define Ut_fail() ut_assert_true( __FILE__, __func__, __LINE__) + +void _ut_assert_equals_double(double expected, double actual); +void _ut_assert_equals_long(long expected, long actual); +void _ut_assert_equals_string(const char* expected, const char* actual); +void _ut_assert_equals_memory(void* expected, void* actual, size_t size); + +void ut_assert_null(void* object, const char* file, const char* func, int line); +void ut_assert_not_null(void* object, const char* file, const char* func, int line); +void ut_assert_false(bool condition, const char* file, const char* func, int line); +void ut_assert_true( bool condition, const char* file, const char* func, int line); +void ut_fail( const char* file, const char* func, int line); + +#endif // UT_ASSERT_H diff --git a/modules/libut/src/ut.c b/modules/libut/src/ut.c new file mode 100644 index 0000000..9c6f519 --- /dev/null +++ b/modules/libut/src/ut.c @@ -0,0 +1,7 @@ +/** + * @file ut_c.c + * @brief Unittest for C モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + diff --git a/modules/main/Makefile b/modules/main/Makefile index ea1562c..6769550 100644 --- a/modules/main/Makefile +++ b/modules/main/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = main TARGET = $(NAME) -SUBDIRS = +SUBDIRS = ut USE_SO_VERSION = # ------------------------------------------------------------------------------ @@ -33,7 +33,7 @@ CFLAGS += CXXFLAGS += LDFLAGS += -LIBS += -L$(TOPDIR)/lib -lkcpp +LIBS += -L$(TOPDIR)/lib -lkc CLEAN_FILES += CLEAN_DIRS += diff --git a/modules/main/include/lang_token.h b/modules/main/include/lang_token.h new file mode 100644 index 0000000..8bffea6 --- /dev/null +++ b/modules/main/include/lang_token.h @@ -0,0 +1,100 @@ +#ifndef LANG_TOKEN_H +#define LANG_TOKEN_H + + +/** + * 扱うトークン種別。 + */ +enum TokenType +{ + TT_UNKNOWN, //< Unknown + + TT_EOF, //< End Of File + TT_EOL, //< End Of Line + TT_SPACE, //< Space (\u0020, \u0009, \u000b, \u000c) + + // BLOCK + // /+ --- +/ LV 0, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` + // + TT_COMMENT, //< Comment + + + TT_IDENTIFIER, //< 識別子 + TT_STRING, //< 文字列リテラル + TT_COMMAND, //< コマンドリテラル + TT_CHARACTER, //< 文字リテラル + TT_INTEGER, //< 整数リテラル + TT_FLOAT, //< 浮動小数リテラル + TT_KEYWORD, //< キーワード + + TT_NOT, //< ! + TT_NOT_E, //< != + TT_NOT_LT, //< !< + TT_NOT_GT, //< !> + TT_NOT_LT_E, //< !<= + TT_NOT_GT_E, //< !>= + TT_NOT_LT_GT, //< !<> + TT_NOT_LT_GT_E, //< !<>= + + TT_LT, //< < + TT_ARROW_L, //< <- + TT_LT_E, //< <= + TT_LT_D, //< << + TT_LT_GT, //< <> + TT_LT_D_E, //< <<= + TT_LT_GT_E, //< <>= + TT_HTML_COMMENT_S, //< + + TT_PLUS, //< + + TT_PLUS_E, //< += + TT_PLUS_D, //< ++ + + TT_SLASH, //< / + TT_SLASH_E, //< /= + + +} + + +#endif // LANG_TOKEN_H diff --git a/modules/main/include/sab_parser.h b/modules/main/include/sab_parser.h new file mode 100644 index 0000000..2d2d41c --- /dev/null +++ b/modules/main/include/sab_parser.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Simple API for Block Parser +// +#ifndef SAB_PARSER_H +#define SAB_PARSER_H + +#include + + +/** + * + */ +typedef struct _SABParserBlock +{ + bool allowInnerBock; //< 内部のブロックを許容する + bool allowNesting; //< ネストを許容する + bool canEscape; //< ESCAPE 可能 + const char* sString; //< 開始文字列 + const char* eString; //< 終了文字列 + void* info; //< 任意の追加情報 +} SABParserBlock; + +typedef struct _LinkedStream +{ + int lineNo; + char* line; + struct _LinkedStream* _next; + char lineData[]; +} + +typedef struct _SABParserConfig +{ + // Block 情報 + SABParserBlock* lv1Blocks; //< Lv1のブロックリスト + size_t lv1BlocksSize; //< Lv1のブロックリストサイズ + SABParserBlock* lv2Blocks; //< Lv2のブロックリスト + size_t lv2BlocksSize; //< Lv2のブロックリストサイズ + SABParserBlock* lv3Blocks; //< Lv3のブロックリスト + size_t lv3BlocksSize; //< Lv3のブロックリストサイズ + +} SABParserConfig; + + + +typedef struct _SABParserBlockStream +{ +} +typedef struct _LineInfo +{ + int no; + const char* line; +} LineInfo; + +LineBasedStream +{ + LineInfo* nextLine(); +} + +void sab_parser_parser(SABParserConfig* config, char*,void (*handler)(SABParserBlock* block, KKcStream* stream) +{ + KcStream* + +} + +#endif // SAB_PARSER_H + + // BLOCK + // /+ --- +/ LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 可 + // # --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // // --- [EOL] LV 0, INNER BLOCK : 許可, ESCAPE 不可, NEST 不可 + // /* --- */ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // ``` --- ``` LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // """ --- """ LV 0, INNER BLOCK : 不可, ESCAPE 不可, NEST 不可 + // { --- } LV 0, ESCAPE 不可, NEST 可 + // ( --- ) LV 0, ESCAPE 不可, NEST 可 + // [ --- ] LV 0, ESCAPE 不可, NEST 可 + // < --- > LV 0, ESCAPE 不可, NEST 可 + // ` --- ` LV 1, ESCAPE 可, NEST 不可 + // ' --- ' LV 2, ESCAPE 可, NEST 不可 + // " --- " LV 2, ESCAPE 可, NEST 不可 + // + // 自分より LV の高いブロックは中に入れ込むことができる。 + // Ex.1) { "abc" : " } " } + // Ex.2) `echo "exec `command`"` +#ifndef LANG_BLOCK_PARSER_HPP +#define LANG_BLOCK_PARSER_HPP + + +/** + * ブロック情報。 + */ +typedef struct +{ + bool allow_innter_block; //!< 中のブロックを許容する + bool allow_nesting; //!< ネストを許容する + bool can_escape; //!< ESCAPE可能 + int lv; //!< ブロックレベル + const char* s_str; //!< 開始文字列 + const char* e_str; //!< 終了文字列 +} BlockInfo; + + +typedef struct +{ + int block_info_id; + unsigned char* data; +} BlockData; + + + +#endif // LANG_BLOCK_PARSER_HPP + diff --git a/modules/main/main b/modules/main/main new file mode 100755 index 0000000..a15caa0 --- /dev/null +++ b/modules/main/main Binary files differ diff --git a/modules/main/obj/lang_block_parser.d b/modules/main/obj/lang_block_parser.d new file mode 100644 index 0000000..46b083d --- /dev/null +++ b/modules/main/obj/lang_block_parser.d @@ -0,0 +1 @@ +obj/lang_block_parser.o: src/lang_block_parser.cpp diff --git a/modules/main/obj/lang_block_parser.o b/modules/main/obj/lang_block_parser.o new file mode 100644 index 0000000..1f10ff9 --- /dev/null +++ b/modules/main/obj/lang_block_parser.o Binary files differ diff --git a/modules/main/obj/main.d b/modules/main/obj/main.d new file mode 100644 index 0000000..ac4fef1 --- /dev/null +++ b/modules/main/obj/main.d @@ -0,0 +1,8 @@ +obj/main.o: src/main.c ../../include/kc_memory.h ../../include/kc.h \ + ../../include/kc_windows.h ../../include/kc_list.h \ + ../../include/kc_macro.h +../../include/kc_memory.h: +../../include/kc.h: +../../include/kc_windows.h: +../../include/kc_list.h: +../../include/kc_macro.h: diff --git a/modules/main/obj/main.o b/modules/main/obj/main.o new file mode 100644 index 0000000..e2dcc49 --- /dev/null +++ b/modules/main/obj/main.o Binary files differ diff --git a/modules/main/src/lang_block_parser.cpp b/modules/main/src/lang_block_parser.cpp new file mode 100644 index 0000000..2b1b98a --- /dev/null +++ b/modules/main/src/lang_block_parser.cpp @@ -0,0 +1,12 @@ + + +class LangBlockParser +{ + public: + LangBlockParser(); + virtual ~LangBlockParser(); + private: +}; + + + diff --git a/modules/main/src/main.c b/modules/main/src/main.c new file mode 100644 index 0000000..d416ff6 --- /dev/null +++ b/modules/main/src/main.c @@ -0,0 +1,71 @@ +#include +#include + +#include + +#include + +extern KcList* KcArrayList_new_ArrayList(size_t size, int cap); + +bool handler(const char* data) +{ + printf("%s\n", data); + return true; +} + +typedef struct +{ + int val1; + int val2; +} VVV; + +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + VVV v1 = { .val1 = 1, .val2 = 11 }; + VVV v2 = { .val1 = 2, .val2 = 22 }; + VVV v3 = { .val1 = 3, .val2 = 33 }; + VVV v4 = { .val1 = 4, .val2 = 44 }; + VVV v5 = { .val1 = 5, .val2 = 55 }; + + KcList* list = KcArrayList_new_ArrayList(sizeof(VVV), 5); + bool ret = list->add(list, list->size(list), &v1, 0); +printf("ret = %d\n", ret); + list->add(list, list->size(list), &v2, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v3, 0); +printf("size=%d\n", list->size(list)); + list->add(list, list->size(list), &v4, 0); +printf("size=%d\n", list->size(list)); + +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// + list->remove(list, 2, NULL, NULL); +//// +printf("-----\n"); +for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); +} +//// +printf("size=%d\n", list->size(list)); + list->add(list, 1, &v5, 0); + + printf("-----\n"); + for (int i = 0; i < list->size(list); i++) { + VVV* gval = list->get(list, i, NULL); + printf("[%d] %d %d\n", i, gval->val1, gval->val2); + } + + + + return 0; +} diff --git a/modules/main/src/main.cpp b/modules/main/src/main.cpp deleted file mode 100644 index 0dc5529..0000000 --- a/modules/main/src/main.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include - -#include -#include - - -class MyListener : public kcpp::MemoryListener -{ - public: - MyListener() {} - ~MyListener() {} - void notifyError(const kcpp::MemoryEntry& entry, const char* msg) - { - std::cout << entry.file << ":" << entry.line << "(" << entry.func << "):[size=" << entry.size << "] " - << msg << std::endl; - } - -}; - -bool handler(const kcpp::MemoryEntry& entry) -{ - std::cout << "# " << entry.file << ":" << entry.line << ":(" << entry.size << ")" << std::endl; - return false; -} - -struct alignas(256) OverAligned { - int val[8192]; - char cval[8192]; -}; -int main() -{ - - MyListener listener; - kcpp::MemoryManager::setListener(listener); - - char* tmp1 = new char; - char* tmp2 = new (std::nothrow) char; - OverAligned* tmp3 = new OverAligned; - tmp3->val[0] = 10; - tmp3->val[8000] = 8010; - tmp3->cval[0] = 'A'; - tmp3->cval[1] = '\0'; - std::cout << tmp3->cval << std::endl; - char* tmp4 = new char[5]; - tmp4[0] = 'X'; - tmp4[1] = 'Y'; - tmp4[2] = 'Z'; - tmp4[3] = '\0'; - char* ptr = static_cast(malloc(10)); - char* ptr2 = static_cast(malloc(20)); - char* ptr3 = static_cast(realloc(ptr2, 15)); - char* ptr4 = static_cast(realloc(tmp1, 55)); - -std::cout << "#####################################" << std::endl; -// kcpp::MemoryManager::entries(handler); - kcpp::MemoryManager::dump(std::cout, 4, true, true, 80); //, 100, true, true); -std::cout << "#####################################" << std::endl; - kcpp::MemoryManager::freeif(handler); -std::cout << "#####################################" << std::endl; - -// free(ptr3); -std::cout << "-- 1" << std::endl; - free(ptr4); -std::cout << "-- 2" << std::endl; - free(tmp2); -std::cout << "-- 3" << std::endl; - - std::cout << "========== delete OverAligned" << std::endl; - delete tmp3; - std::cout << "========== END delete OverAligned" << std::endl; -// operator delete(tmp3); - - std::cout << tmp4 << std::endl; - - std::cout << "========== delete tmp4[5]" << std::endl; - delete[] tmp4; - std::cout << "========== END delete tmp4[5]" << std::endl; - - (void)(ptr); - (void)(ptr2); - (void)(ptr3); - (void)(ptr4); - - char* tmp5 = new char[100]; - tmp5[0] = 'A'; - tmp5[1] = '\0'; - std::cout << tmp5[0] << std::endl; - delete [] tmp5; - - - return 0; - -} - diff --git a/modules/main/ut/Makefile b/modules/main/ut/Makefile new file mode 100644 index 0000000..cb97620 --- /dev/null +++ b/modules/main/ut/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut.exe +TARGET = $(NAME) +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib -lkcpp -ldl + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/main/ut/src/ut_kc_memory_dump.cpp b/modules/main/ut/src/ut_kc_memory_dump.cpp new file mode 100644 index 0000000..b07eec7 --- /dev/null +++ b/modules/main/ut/src/ut_kc_memory_dump.cpp @@ -0,0 +1,107 @@ +#include +#include + +#include + +using namespace kcpp; +using namespace kcpp::Assert; +using namespace kc; + +/** + * KcMemoryDump 単体テスト。 + */ +class UtKcMemoryDump: public TestCase +{ + public: + UtKcMemoryDump() : entry() + { /* NOP */ } + ~UtKcMemoryDump() {/* NOP */ } + + void setUp() + { + static char data_buff[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + entry.file = "file"; + entry.func = "func"; + entry.line = 123; + entry.size = 52; + entry.data = data_buff; + + snprintf(expected_info , sizeof(expected_info) , "file:123 (10 bytes) [func=func]"); + snprintf(expected_binary, sizeof(expected_binary), " | 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F"); + + } + + void testSample1() + { + int buff_size = 81;//sizeof(test_buff); + int bytes = 60; + bool binary = true; + bool ascii = true; + int column = 80; + + kc_memory_dump(test_buff, buff_size, &entry, bytes, binary, ascii, column); + + // カラム数が合致するか確認 + printf(" 10 20 30 40 50 60 70 80 90\n"); + printf("----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|\n"); + printf("%s[EOL]\n", test_buff); + } + void testSample2() + { + } + void run() + { + RUN_TEST(testSample1, "sample1"); + } + private: + KcMemoryEntry entry; + char test_buff[4096]; + char expected_info[4096]; + char expected_binary[4096]; + char expected_ascii[4096]; +}; + +#if 0 +#ifndef UNITTEST +int main() +#else +int dummy() +#endif +{ + char buff[1024]; + KcMemoryEntry entry = { + .file = "file", + .func = "func", + .line = 123, + .size = 10, + .data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + }; + + TestInfo testInfoList[] = + { + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = false, .max_column = 80 }, + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = true, .max_column = 80 }, + { .buff_size = 100, .bytes = 16, .binary = true, .ascii = true, .max_column = 79 }, + + + /* 番兵 */ + { .buff_size = -1, .bytes = -1, .binary = false, .ascii = false, .max_column = -1 } + }; + + for (TestInfo* testInfo = &testInfoList[0]; testInfo->buff_size >= 0; testInfo++) + { + + kc_memory_dump(buff, testInfo->buff_size, &entry, testInfo->bytes, testInfo->binary, testInfo->ascii, testInfo->max_column); + + printf("####################################################\n"); + printf("buff_size = %d\n", testInfo->buff_size); + printf("bytes = %d\n", testInfo->bytes); + printf("max_column= %d\n", testInfo->max_column); + printf(" 10 20 30 40 50 60 70 80 90 \n"); + printf("----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|----+----|\n"); + printf("%s[END]\n", buff); + + } + return 0; +} +#endif diff --git a/modules/main/ut/src/ut_main.cpp b/modules/main/ut/src/ut_main.cpp new file mode 100644 index 0000000..f1c3f90 --- /dev/null +++ b/modules/main/ut/src/ut_main.cpp @@ -0,0 +1,16 @@ +#include + +#include "ut_kc_memory_dump.cpp" + + +int main() +{ + UtKcMemoryDump memory_dump; + + memory_dump.run(); + + kcpp::utManager.printResult(); + + return 0; +} + diff --git a/modules/main/ut/src/ut_sample.cpp b/modules/main/ut/src/ut_sample.cpp new file mode 100644 index 0000000..72b5582 --- /dev/null +++ b/modules/main/ut/src/ut_sample.cpp @@ -0,0 +1,30 @@ +#include + +#include + +using namespace kcpp; + +class UtSample : public TestCase +{ + public: + UtSample() {} + ~UtSample() {} + void setUp() + { + std::cout << "setup" << std::endl; + } + void testSample1() + { + Assert::assertEquals(1,1); + } + void testSample2() + { + Assert::assertTrue(false); +// Assert::fail(); + } + void run() + { + RUN_TEST(testSample1, "sample1"); + RUN_TEST(testSample2, "sample2"); + } +}; diff --git a/modules/test.c b/modules/test.c new file mode 100644 index 0000000..f18dbdf --- /dev/null +++ b/modules/test.c @@ -0,0 +1,27 @@ +#include + +#define kc_add(a,b) _add_1(list, a, b) + + +struct +{ + int (*_add_1)(int a, int b); +} Tmp; + +int Tmp_add(int a, int b) { + return (a+b); +} + +int main() +{ + Tmp* tmp = (Tmp*) malloc(sizeof(Tmp)); + tmp->_add_1 = Tmp_add; + + + int ret =tmp->kc_add(1,2); + printf("ret = %d\n", ret); + + + return 0; +} +