diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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 new file mode 100644 index 0000000..e96259a --- /dev/null +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + AssertError() noexcept; + AssertError(const AssertError& t) noexcept; + AssertError(const std::string& msg) noexcept; + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + virtual ~AssertError() noexcept; + const std::string& getFile() const noexcept; + const std::string& getFunc() const noexcept; + int getLine() const noexcept; + private: + std::string errorFile; + std::string errorFunc; + int errorLine; + }; + + namespace Assert + { + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + void assertTrue (bool condition , const char* file, const char* func, int line); + void assertFalse (bool condition , const char* file, const char* func, int line); + void assertNull (void* obj , const char* file, const char* func, int line); + void assertNotNull(void* obj , const char* file, const char* func, int line); + void fail( 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 fail() fail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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 new file mode 100644 index 0000000..e96259a --- /dev/null +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + AssertError() noexcept; + AssertError(const AssertError& t) noexcept; + AssertError(const std::string& msg) noexcept; + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + virtual ~AssertError() noexcept; + const std::string& getFile() const noexcept; + const std::string& getFunc() const noexcept; + int getLine() const noexcept; + private: + std::string errorFile; + std::string errorFunc; + int errorLine; + }; + + namespace Assert + { + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + void assertTrue (bool condition , const char* file, const char* func, int line); + void assertFalse (bool condition , const char* file, const char* func, int line); + void assertNull (void* obj , const char* file, const char* func, int line); + void assertNotNull(void* obj , const char* file, const char* func, int line); + void fail( 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 fail() fail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/modules/libkcpp/include/kcpp_error.hpp b/modules/libkcpp/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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 new file mode 100644 index 0000000..e96259a --- /dev/null +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + AssertError() noexcept; + AssertError(const AssertError& t) noexcept; + AssertError(const std::string& msg) noexcept; + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + virtual ~AssertError() noexcept; + const std::string& getFile() const noexcept; + const std::string& getFunc() const noexcept; + int getLine() const noexcept; + private: + std::string errorFile; + std::string errorFunc; + int errorLine; + }; + + namespace Assert + { + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + void assertTrue (bool condition , const char* file, const char* func, int line); + void assertFalse (bool condition , const char* file, const char* func, int line); + void assertNull (void* obj , const char* file, const char* func, int line); + void assertNotNull(void* obj , const char* file, const char* func, int line); + void fail( 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 fail() fail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/modules/libkcpp/include/kcpp_error.hpp b/modules/libkcpp/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_exception.hpp b/modules/libkcpp/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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 new file mode 100644 index 0000000..e96259a --- /dev/null +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + AssertError() noexcept; + AssertError(const AssertError& t) noexcept; + AssertError(const std::string& msg) noexcept; + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + virtual ~AssertError() noexcept; + const std::string& getFile() const noexcept; + const std::string& getFunc() const noexcept; + int getLine() const noexcept; + private: + std::string errorFile; + std::string errorFunc; + int errorLine; + }; + + namespace Assert + { + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + void assertTrue (bool condition , const char* file, const char* func, int line); + void assertFalse (bool condition , const char* file, const char* func, int line); + void assertNull (void* obj , const char* file, const char* func, int line); + void assertNotNull(void* obj , const char* file, const char* func, int line); + void fail( 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 fail() fail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/modules/libkcpp/include/kcpp_error.hpp b/modules/libkcpp/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_exception.hpp b/modules/libkcpp/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_throwable.hpp b/modules/libkcpp/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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 new file mode 100644 index 0000000..e96259a --- /dev/null +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + AssertError() noexcept; + AssertError(const AssertError& t) noexcept; + AssertError(const std::string& msg) noexcept; + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + virtual ~AssertError() noexcept; + const std::string& getFile() const noexcept; + const std::string& getFunc() const noexcept; + int getLine() const noexcept; + private: + std::string errorFile; + std::string errorFunc; + int errorLine; + }; + + namespace Assert + { + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + void assertTrue (bool condition , const char* file, const char* func, int line); + void assertFalse (bool condition , const char* file, const char* func, int line); + void assertNull (void* obj , const char* file, const char* func, int line); + void assertNotNull(void* obj , const char* file, const char* func, int line); + void fail( 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 fail() fail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/modules/libkcpp/include/kcpp_error.hpp b/modules/libkcpp/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_exception.hpp b/modules/libkcpp/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_throwable.hpp b/modules/libkcpp/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/src/kc_memory.cpp b/modules/libkcpp/src/kc_memory.cpp deleted file mode 100644 index d33bced..0000000 --- a/modules/libkcpp/src/kc_memory.cpp +++ /dev/null @@ -1,1224 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール (C++) -// @copyright 2003 - 2023 Nomura Kei -// - -#include -#include -#include -#include -#include - -// 常に本来の malloc, free を利用するため、KCPP_MEMORY_ENABLED を無効化する。 -#ifdef KCPP_MEMORY_ENABLED -#undef KCPP_MEMORY_ENABLED -#endif -#include - - -using namespace kcpp; -namespace kcpp -{ - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryListener - // - - /** - * MemoryListener を構築します。 - */ - MemoryListener::MemoryListener() { - // NOP - } - - - /** - * MemoryListener を破棄します。 - */ - MemoryListener::~MemoryListener() - { - // NOP - } - - - /** - * メモリ確保時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyAllocate([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * メモリ解放時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyFree([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * エラー発生時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyError([[maybe_unused]] const MemoryEntry& entry, [[maybe_unused]] const char* msg) - { - // NOP - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntry ユーティリティ (initMemoryEntry) - // - - /** - * メモリエントリに指定されたパラメータを設定、初期化します。 - * - * @param entry 初期化設定するメモリエントリ - * @param size 確保サイズ - * @param mark 確保メモリ状態 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ - void initMemoryEntry(MemoryEntry* entry, - std::size_t size, MemoryMark 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->_prev = nullptr; - entry->_next = nullptr; - entry->data = (entry + 1); - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntryManager (内部クラス) - // メモリのエントリを管理します。 - // - - /** - * メモリエントリを管理するクラス。 - */ - class MemoryEntryManager - { - public: - static constexpr int MAX_BUFFER_SIZE = 256; - - MemoryEntryManager(); - virtual ~MemoryEntryManager(); - void add(MemoryEntry* entry); - void remove(MemoryEntry* entry); - void entries(bool (*handler)(const MemoryEntry& entry)); - void freeif(bool (*handler)(const MemoryEntry& entry)); - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn); - private: - void dumpBinary(std::ostream& stream, void* data, size_t size, int limit); - void dumpAscii(std::ostream& stream, void* data, size_t size, int limit); - const char* toPaddingString(const char* str, int limit); - const char* toHexString(unsigned char data); - MemoryEntryManager(const MemoryEntryManager& mgr) = delete; - MemoryEntryManager& operator=(const MemoryEntryManager& mgr) = delete; - MemoryEntry head; - MemoryEntry tail; - std::recursive_mutex entryMutex; - char tmpbuf[MAX_BUFFER_SIZE]; - }; - - - /** - * MemoryEntryManager を構築します。 - */ - MemoryEntryManager::MemoryEntryManager() : - head { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - tail { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - entryMutex(), - tmpbuf{ 0 } - { - initMemoryEntry(&head, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - initMemoryEntry(&tail, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - head._prev = head._next = &tail; - tail._prev = tail._next = &head; - } - - - /** - * MemoryEntryManager を破棄します。 - */ - MemoryEntryManager::~MemoryEntryManager() - { - // NOP - } - - - /** - * 指定されたメモリエントリを追加します。 - * - * @param entry 追加するメモリエントリ - */ - void MemoryEntryManager::add(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // [tail] の一つ前に挿入する - entry->_next = &tail; - entry->_prev = tail._prev; - tail._prev->_next = entry; - tail._prev = entry; - } - - - /** - * 指定されたメモリエントリを削除します。 - * - * @param entry 削除するメモリエントリ - */ - void MemoryEntryManager::remove(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // entry の前後を直接リンクさせる - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::entries(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isContinue = true; - for (MemoryEntry* current = head._next; isContinue && (current != &tail); current = current->_next) - { - isContinue = handler(*current); - } - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::freeif(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isFree = false; - for (MemoryEntry* current = head._next; current != &tail; ) - { - isFree = handler(*current); - current = current->_next; - if (isFree) - { - MemoryManager::free(current->_prev->data); - } - } - } - - - /** - * 管理しているメモリエントリをダンプします。 - * - * @param stream ダンプ出力先ストリーム - * @param width 幅 - * @param isDumpBinary バイナリダンプ - * @param isDumpAscii ASCII ダンプ - * @param maxColumn 最大桁数 - */ - void MemoryEntryManager::dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - std::lock_guard lock(entryMutex); - - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分の最大表示桁数を取得する。 - int infoColumn = maxColumn; - infoColumn -= (isDumpBinary) ? (dumpByte * 3) + 2 : 0; - infoColumn -= (isDumpAscii) ? (dumpByte ) + 3 : 0; - if (infoColumn < 0) - { - infoColumn = 0; - } - - // 管理している全メモリエントリをループ - for (MemoryEntry* current = head._next; current != &tail; current = current->_next) - { - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分出力 - std::stringstream ss; - ss << current->file << ":" << current->line << " (size=" << current->size << ")" - << " [func=" << current->func << "]"; - stream << toPaddingString(ss.str().c_str(), infoColumn); - - // 16進数ダンプ - if (isDumpBinary) - { - stream << " | "; - dumpBinary(stream, current->data, current->size, dumpByte); - } - - // ASCII ダンプ - if (isDumpAscii) - { - stream << " | "; - dumpAscii(stream, current->data, current->size, dumpByte); - } - - stream << std::endl; - } - } - - - /** - * 指定されたデータを指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpBinary(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - if (idx != 0) { stream << " "; } - stream << toHexString(dataPtr[idx]); - } - for (; idx < limit; idx++) - { - if (idx != 0) { stream << " "; } - stream << "--"; - } - } - - - /** - * 指定されたデータを ASCII 文字で、指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpAscii(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - stream << static_cast((((0x20 <= dataPtr[idx]) && (dataPtr[idx] < 0x7F)) ? dataPtr[idx] : '.')); - } - for (; idx < limit; idx++) - { - stream << " "; - } - } - - /** - * 指定された文字列を指定された文字数にパディングします。 - * 指定された limit より文字数が多い場合は、limit まで文字列が切り詰められます。 - * - * @param str 文字列 - * @param limit 文字数 - * @return 制限された文字列 - */ - const char* MemoryEntryManager::toPaddingString(const char* str, int limit) - { - int maxLimit = (limit <= (MAX_BUFFER_SIZE - 1)) ? limit : (MAX_BUFFER_SIZE - 1); - int len = std::strlen(str); - if (len < maxLimit) - { - memcpy(tmpbuf, str, len); - memset((tmpbuf + len), ' ', (maxLimit - len)); - } - else - { - memcpy(tmpbuf, str, maxLimit); - } - tmpbuf[maxLimit] = '\0'; - return tmpbuf; - } - - - /** - * 指定されたデータを16進数文字列に変換します。 - * - * @param data 変換するデータ - * @return 16進数文字列 - */ - const char* MemoryEntryManager::toHexString(unsigned char data) - { - static const char* HEX_STRINGS = "0123456789ABCDEF"; - tmpbuf[0] = HEX_STRINGS[(static_cast(data) >> 4) & 0x0F]; - tmpbuf[1] = HEX_STRINGS[ static_cast(data) & 0x0F]; - tmpbuf[2] = '\0'; - return tmpbuf; - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryManager - // メモリ管理 - // - - namespace MemoryManager - { - namespace - { - // ================================================================= - // 内部定数 - // ================================================================= - int const PADDING = sizeof(void*) * 2; - - // ================================================================= - // 内部関数プロトタイプ宣言 - // ================================================================= - std::new_handler getNewHandler(); - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void* allocate(std::size_t size, std::align_val_t align, MemoryMark mark, const char* file, const char* func, int line); - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void deallocate(void* ptr, MemoryMark expectedMark); - - // reallocate から呼び出される関数 - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateInvalidPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - - // deallocate から呼び出される関数 - void deallocateEntry(MemoryEntry* entry); - - - // ================================================================= - // 内部変数 - // ================================================================= - MemoryListener defaultListener; - MemoryListener* listener = &defaultListener; //!< メモリ管理リスナ - MemoryEntryManager entryMgr; - - } - - - // ================================================================= - // new 実施時の情報を一時保持するための変数 - // ================================================================= - thread_local const char* file; //!< ファイル名 - thread_local const char* func; //!< 関数 - thread_local int line; //!< 行番号 - - - /** - * メモリ確保, 解放, エラー発生時に通知を受信するリスナを登録します。 - * - * @param l 登録するリスナ - */ - void setListener(MemoryListener& l) - { - listener = &l; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void entries(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.entries(handler); - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void freeif(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.freeif(handler); - } - - - /** - * 管理しているメモリエントリ情報をダンプします。 - * - * @param stream ダンプ先ストリーム - * @param dumpByte ダンプするバイト数 - * @param isDumpBinary バイナリをダンプする - * @param isDumpAscii アスキーをダンプする - * @param maxColumn 最大表示桁数 - */ - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - entryMgr.dump(stream, dumpByte, isDumpBinary, isDumpAscii, maxColumn); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* malloc(std::size_t size, const char* file, const char* func, int line) - { - void* ptr = allocate(size, MEMORY_MARK_ALLOCATED, file, func, line); - return ptr; - } - - - /** - * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 - * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* cmalloc(std::size_t nmemb, std::size_t size, const char* file, const char* func, int line) - { - size_t n = nmemb * size; - void* ptr = allocate(n, MEMORY_MARK_ALLOCATED, file, func, line); - if (ptr != nullptr) - { - std::memset(ptr, 0x00, n); - } - return ptr; - } - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* realloc(void* ptr, std::size_t size, const char* file, const char* func, int line) - { - void* nptr = reallocate(ptr, size, MEMORY_MARK_ALLOCATED, file, func, line); - return nptr; - } - - - /** - * 指定されたメモリを解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ - void free(void* ptr) - { - deallocate(ptr, MEMORY_MARK_ALLOCATED); - } - - - namespace - { - // ================================================================= - // 内部関数の実装 - // ================================================================= - - /** - * new_handler を取得します。 - * @return new_handler - */ - std::new_handler getNewHandler() - { - std::new_handler p = std::set_new_handler(0); - std::set_new_handler(p); - return p; - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::malloc(size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param align アライメント - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, std::align_val_t align, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::aligned_alloc(static_cast(align), size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * ポインタが nullptr の場合、allocate を呼び出します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - if (ptr == nullptr) - { - return allocate(size, mark, file, func, line); - } - - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - switch (oldEntry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み -> 通常の allocate と同様とする。 - return allocate(size, mark, file, func,line); - case MEMORY_MARK_ALLOCATED: // 管理されたメモリの realloc - return reallocateManagedPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - default: // 管理外メモリの realloc - return reallocateUnManagedPtr(ptr, size, mark, file, func, line); - } - } - - - /** - * 指定されたポインタの指すメモリ領域を解放します。 - * nullptr の場合何もしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、fee を実行します。 - * - * @param ptr 解放するメモリへのポインタ - * @param expectedMark 期待するメモリ確保情報 - */ - void deallocate(void* ptr, MemoryMark expectedMark) - { - if (ptr == nullptr) - { - return; - } - - MemoryEntry* entry = static_cast(ptr); - entry--; - if (entry->_mark == expectedMark) - { // 期待するメモリ確保情報の場合、そのまま解放する。 - deallocateEntry(entry); - } - else - { // 期待しないメモリ確保情報の場合 - switch (entry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み - // Nothing to do. - break; - case MEMORY_MARK_ALLOCATED: // 管理メモリ - listener->notifyError(*entry, "warning free memory (please use free)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW: // new により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete[])"); - deallocateEntry(entry); - break; - default: // 管理外メモリ - std::free(ptr); - break; - } - } - } - - - /** - * 管理されたメモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - - // メモリのエントリより削除する - entryMgr.remove(oldEntry); - MemoryEntry* entry = static_cast(std::realloc(oldEntry, size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) - { // メモリ確保成功 - // -> 管理領域の情報を更新して、メモリのエントリとして追加する。 - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で 元々の領域 + 追加分 を、 - // ↓管理領域分を確保した先にコピーする - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - MemoryEntry* entry = static_cast(std::realloc(ptr, size + sizeof(MemoryEntry) + PADDING)); - if (entry != NULL) - { // メモリ確保成功 - // memmove で 元々の領域 + 追加分をコピーして、メモリのエントリとして追加する。 - std::memmove((entry + 1), entry, size); - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ(nullptr 固定) - */ - void* reallocateInvalidPtr([[maybe_unused]] void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - errno = EINVAL; - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate (invalid pointer)"); - return nullptr; - } - - - /** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ - void deallocateEntry(MemoryEntry* entry) - { - listener->notifyFree(*entry); - entry->_mark = MEMORY_MARK_DELETED; - entry->size = 0; - entryMgr.remove(entry); - std::free(entry); - } - } - - } - -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// new/delete 演算子のオーバライド -// - -// C++17 (C++1z) 以降の new/delete 演算子 - - -// ============================================================================= -// new 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -// ============================================================================= -// new[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new[] による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - -// ============================================================================= -// delete 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - - -// ============================================================================= -// delete[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete[] により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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 new file mode 100644 index 0000000..e96259a --- /dev/null +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + AssertError() noexcept; + AssertError(const AssertError& t) noexcept; + AssertError(const std::string& msg) noexcept; + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + virtual ~AssertError() noexcept; + const std::string& getFile() const noexcept; + const std::string& getFunc() const noexcept; + int getLine() const noexcept; + private: + std::string errorFile; + std::string errorFunc; + int errorLine; + }; + + namespace Assert + { + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + void assertTrue (bool condition , const char* file, const char* func, int line); + void assertFalse (bool condition , const char* file, const char* func, int line); + void assertNull (void* obj , const char* file, const char* func, int line); + void assertNotNull(void* obj , const char* file, const char* func, int line); + void fail( 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 fail() fail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/modules/libkcpp/include/kcpp_error.hpp b/modules/libkcpp/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_exception.hpp b/modules/libkcpp/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_throwable.hpp b/modules/libkcpp/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/src/kc_memory.cpp b/modules/libkcpp/src/kc_memory.cpp deleted file mode 100644 index d33bced..0000000 --- a/modules/libkcpp/src/kc_memory.cpp +++ /dev/null @@ -1,1224 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール (C++) -// @copyright 2003 - 2023 Nomura Kei -// - -#include -#include -#include -#include -#include - -// 常に本来の malloc, free を利用するため、KCPP_MEMORY_ENABLED を無効化する。 -#ifdef KCPP_MEMORY_ENABLED -#undef KCPP_MEMORY_ENABLED -#endif -#include - - -using namespace kcpp; -namespace kcpp -{ - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryListener - // - - /** - * MemoryListener を構築します。 - */ - MemoryListener::MemoryListener() { - // NOP - } - - - /** - * MemoryListener を破棄します。 - */ - MemoryListener::~MemoryListener() - { - // NOP - } - - - /** - * メモリ確保時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyAllocate([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * メモリ解放時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyFree([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * エラー発生時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyError([[maybe_unused]] const MemoryEntry& entry, [[maybe_unused]] const char* msg) - { - // NOP - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntry ユーティリティ (initMemoryEntry) - // - - /** - * メモリエントリに指定されたパラメータを設定、初期化します。 - * - * @param entry 初期化設定するメモリエントリ - * @param size 確保サイズ - * @param mark 確保メモリ状態 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ - void initMemoryEntry(MemoryEntry* entry, - std::size_t size, MemoryMark 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->_prev = nullptr; - entry->_next = nullptr; - entry->data = (entry + 1); - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntryManager (内部クラス) - // メモリのエントリを管理します。 - // - - /** - * メモリエントリを管理するクラス。 - */ - class MemoryEntryManager - { - public: - static constexpr int MAX_BUFFER_SIZE = 256; - - MemoryEntryManager(); - virtual ~MemoryEntryManager(); - void add(MemoryEntry* entry); - void remove(MemoryEntry* entry); - void entries(bool (*handler)(const MemoryEntry& entry)); - void freeif(bool (*handler)(const MemoryEntry& entry)); - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn); - private: - void dumpBinary(std::ostream& stream, void* data, size_t size, int limit); - void dumpAscii(std::ostream& stream, void* data, size_t size, int limit); - const char* toPaddingString(const char* str, int limit); - const char* toHexString(unsigned char data); - MemoryEntryManager(const MemoryEntryManager& mgr) = delete; - MemoryEntryManager& operator=(const MemoryEntryManager& mgr) = delete; - MemoryEntry head; - MemoryEntry tail; - std::recursive_mutex entryMutex; - char tmpbuf[MAX_BUFFER_SIZE]; - }; - - - /** - * MemoryEntryManager を構築します。 - */ - MemoryEntryManager::MemoryEntryManager() : - head { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - tail { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - entryMutex(), - tmpbuf{ 0 } - { - initMemoryEntry(&head, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - initMemoryEntry(&tail, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - head._prev = head._next = &tail; - tail._prev = tail._next = &head; - } - - - /** - * MemoryEntryManager を破棄します。 - */ - MemoryEntryManager::~MemoryEntryManager() - { - // NOP - } - - - /** - * 指定されたメモリエントリを追加します。 - * - * @param entry 追加するメモリエントリ - */ - void MemoryEntryManager::add(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // [tail] の一つ前に挿入する - entry->_next = &tail; - entry->_prev = tail._prev; - tail._prev->_next = entry; - tail._prev = entry; - } - - - /** - * 指定されたメモリエントリを削除します。 - * - * @param entry 削除するメモリエントリ - */ - void MemoryEntryManager::remove(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // entry の前後を直接リンクさせる - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::entries(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isContinue = true; - for (MemoryEntry* current = head._next; isContinue && (current != &tail); current = current->_next) - { - isContinue = handler(*current); - } - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::freeif(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isFree = false; - for (MemoryEntry* current = head._next; current != &tail; ) - { - isFree = handler(*current); - current = current->_next; - if (isFree) - { - MemoryManager::free(current->_prev->data); - } - } - } - - - /** - * 管理しているメモリエントリをダンプします。 - * - * @param stream ダンプ出力先ストリーム - * @param width 幅 - * @param isDumpBinary バイナリダンプ - * @param isDumpAscii ASCII ダンプ - * @param maxColumn 最大桁数 - */ - void MemoryEntryManager::dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - std::lock_guard lock(entryMutex); - - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分の最大表示桁数を取得する。 - int infoColumn = maxColumn; - infoColumn -= (isDumpBinary) ? (dumpByte * 3) + 2 : 0; - infoColumn -= (isDumpAscii) ? (dumpByte ) + 3 : 0; - if (infoColumn < 0) - { - infoColumn = 0; - } - - // 管理している全メモリエントリをループ - for (MemoryEntry* current = head._next; current != &tail; current = current->_next) - { - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分出力 - std::stringstream ss; - ss << current->file << ":" << current->line << " (size=" << current->size << ")" - << " [func=" << current->func << "]"; - stream << toPaddingString(ss.str().c_str(), infoColumn); - - // 16進数ダンプ - if (isDumpBinary) - { - stream << " | "; - dumpBinary(stream, current->data, current->size, dumpByte); - } - - // ASCII ダンプ - if (isDumpAscii) - { - stream << " | "; - dumpAscii(stream, current->data, current->size, dumpByte); - } - - stream << std::endl; - } - } - - - /** - * 指定されたデータを指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpBinary(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - if (idx != 0) { stream << " "; } - stream << toHexString(dataPtr[idx]); - } - for (; idx < limit; idx++) - { - if (idx != 0) { stream << " "; } - stream << "--"; - } - } - - - /** - * 指定されたデータを ASCII 文字で、指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpAscii(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - stream << static_cast((((0x20 <= dataPtr[idx]) && (dataPtr[idx] < 0x7F)) ? dataPtr[idx] : '.')); - } - for (; idx < limit; idx++) - { - stream << " "; - } - } - - /** - * 指定された文字列を指定された文字数にパディングします。 - * 指定された limit より文字数が多い場合は、limit まで文字列が切り詰められます。 - * - * @param str 文字列 - * @param limit 文字数 - * @return 制限された文字列 - */ - const char* MemoryEntryManager::toPaddingString(const char* str, int limit) - { - int maxLimit = (limit <= (MAX_BUFFER_SIZE - 1)) ? limit : (MAX_BUFFER_SIZE - 1); - int len = std::strlen(str); - if (len < maxLimit) - { - memcpy(tmpbuf, str, len); - memset((tmpbuf + len), ' ', (maxLimit - len)); - } - else - { - memcpy(tmpbuf, str, maxLimit); - } - tmpbuf[maxLimit] = '\0'; - return tmpbuf; - } - - - /** - * 指定されたデータを16進数文字列に変換します。 - * - * @param data 変換するデータ - * @return 16進数文字列 - */ - const char* MemoryEntryManager::toHexString(unsigned char data) - { - static const char* HEX_STRINGS = "0123456789ABCDEF"; - tmpbuf[0] = HEX_STRINGS[(static_cast(data) >> 4) & 0x0F]; - tmpbuf[1] = HEX_STRINGS[ static_cast(data) & 0x0F]; - tmpbuf[2] = '\0'; - return tmpbuf; - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryManager - // メモリ管理 - // - - namespace MemoryManager - { - namespace - { - // ================================================================= - // 内部定数 - // ================================================================= - int const PADDING = sizeof(void*) * 2; - - // ================================================================= - // 内部関数プロトタイプ宣言 - // ================================================================= - std::new_handler getNewHandler(); - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void* allocate(std::size_t size, std::align_val_t align, MemoryMark mark, const char* file, const char* func, int line); - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void deallocate(void* ptr, MemoryMark expectedMark); - - // reallocate から呼び出される関数 - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateInvalidPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - - // deallocate から呼び出される関数 - void deallocateEntry(MemoryEntry* entry); - - - // ================================================================= - // 内部変数 - // ================================================================= - MemoryListener defaultListener; - MemoryListener* listener = &defaultListener; //!< メモリ管理リスナ - MemoryEntryManager entryMgr; - - } - - - // ================================================================= - // new 実施時の情報を一時保持するための変数 - // ================================================================= - thread_local const char* file; //!< ファイル名 - thread_local const char* func; //!< 関数 - thread_local int line; //!< 行番号 - - - /** - * メモリ確保, 解放, エラー発生時に通知を受信するリスナを登録します。 - * - * @param l 登録するリスナ - */ - void setListener(MemoryListener& l) - { - listener = &l; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void entries(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.entries(handler); - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void freeif(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.freeif(handler); - } - - - /** - * 管理しているメモリエントリ情報をダンプします。 - * - * @param stream ダンプ先ストリーム - * @param dumpByte ダンプするバイト数 - * @param isDumpBinary バイナリをダンプする - * @param isDumpAscii アスキーをダンプする - * @param maxColumn 最大表示桁数 - */ - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - entryMgr.dump(stream, dumpByte, isDumpBinary, isDumpAscii, maxColumn); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* malloc(std::size_t size, const char* file, const char* func, int line) - { - void* ptr = allocate(size, MEMORY_MARK_ALLOCATED, file, func, line); - return ptr; - } - - - /** - * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 - * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* cmalloc(std::size_t nmemb, std::size_t size, const char* file, const char* func, int line) - { - size_t n = nmemb * size; - void* ptr = allocate(n, MEMORY_MARK_ALLOCATED, file, func, line); - if (ptr != nullptr) - { - std::memset(ptr, 0x00, n); - } - return ptr; - } - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* realloc(void* ptr, std::size_t size, const char* file, const char* func, int line) - { - void* nptr = reallocate(ptr, size, MEMORY_MARK_ALLOCATED, file, func, line); - return nptr; - } - - - /** - * 指定されたメモリを解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ - void free(void* ptr) - { - deallocate(ptr, MEMORY_MARK_ALLOCATED); - } - - - namespace - { - // ================================================================= - // 内部関数の実装 - // ================================================================= - - /** - * new_handler を取得します。 - * @return new_handler - */ - std::new_handler getNewHandler() - { - std::new_handler p = std::set_new_handler(0); - std::set_new_handler(p); - return p; - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::malloc(size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param align アライメント - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, std::align_val_t align, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::aligned_alloc(static_cast(align), size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * ポインタが nullptr の場合、allocate を呼び出します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - if (ptr == nullptr) - { - return allocate(size, mark, file, func, line); - } - - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - switch (oldEntry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み -> 通常の allocate と同様とする。 - return allocate(size, mark, file, func,line); - case MEMORY_MARK_ALLOCATED: // 管理されたメモリの realloc - return reallocateManagedPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - default: // 管理外メモリの realloc - return reallocateUnManagedPtr(ptr, size, mark, file, func, line); - } - } - - - /** - * 指定されたポインタの指すメモリ領域を解放します。 - * nullptr の場合何もしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、fee を実行します。 - * - * @param ptr 解放するメモリへのポインタ - * @param expectedMark 期待するメモリ確保情報 - */ - void deallocate(void* ptr, MemoryMark expectedMark) - { - if (ptr == nullptr) - { - return; - } - - MemoryEntry* entry = static_cast(ptr); - entry--; - if (entry->_mark == expectedMark) - { // 期待するメモリ確保情報の場合、そのまま解放する。 - deallocateEntry(entry); - } - else - { // 期待しないメモリ確保情報の場合 - switch (entry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み - // Nothing to do. - break; - case MEMORY_MARK_ALLOCATED: // 管理メモリ - listener->notifyError(*entry, "warning free memory (please use free)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW: // new により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete[])"); - deallocateEntry(entry); - break; - default: // 管理外メモリ - std::free(ptr); - break; - } - } - } - - - /** - * 管理されたメモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - - // メモリのエントリより削除する - entryMgr.remove(oldEntry); - MemoryEntry* entry = static_cast(std::realloc(oldEntry, size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) - { // メモリ確保成功 - // -> 管理領域の情報を更新して、メモリのエントリとして追加する。 - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で 元々の領域 + 追加分 を、 - // ↓管理領域分を確保した先にコピーする - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - MemoryEntry* entry = static_cast(std::realloc(ptr, size + sizeof(MemoryEntry) + PADDING)); - if (entry != NULL) - { // メモリ確保成功 - // memmove で 元々の領域 + 追加分をコピーして、メモリのエントリとして追加する。 - std::memmove((entry + 1), entry, size); - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ(nullptr 固定) - */ - void* reallocateInvalidPtr([[maybe_unused]] void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - errno = EINVAL; - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate (invalid pointer)"); - return nullptr; - } - - - /** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ - void deallocateEntry(MemoryEntry* entry) - { - listener->notifyFree(*entry); - entry->_mark = MEMORY_MARK_DELETED; - entry->size = 0; - entryMgr.remove(entry); - std::free(entry); - } - } - - } - -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// new/delete 演算子のオーバライド -// - -// C++17 (C++1z) 以降の new/delete 演算子 - - -// ============================================================================= -// new 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -// ============================================================================= -// new[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new[] による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - -// ============================================================================= -// delete 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - - -// ============================================================================= -// delete[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete[] により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp new file mode 100644 index 0000000..cfd47c2 --- /dev/null +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -0,0 +1,386 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Assert +// + +#include + +#include + +#undef assertEquals +#undef assertTrue +#undef assertFalse +#undef assertNull +#undef assertNotNull +#undef fail + +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; + } + + + 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) + { + const char* msg = (expected) + ? "expected: but was:" + : "expected: but was:"; + throw AssertError(msg, file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), 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 std::string& actual, const char* file, const char* func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), 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 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; + std::string actualStr = actual; + 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) + { + throw AssertError("expected: but was:", file, func, line); + } + } + + + /** + * 指定された 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) + { + throw AssertError("expected: but was:", file, func, line); + } + } + + + /** + * 常に、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/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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 new file mode 100644 index 0000000..e96259a --- /dev/null +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + AssertError() noexcept; + AssertError(const AssertError& t) noexcept; + AssertError(const std::string& msg) noexcept; + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + virtual ~AssertError() noexcept; + const std::string& getFile() const noexcept; + const std::string& getFunc() const noexcept; + int getLine() const noexcept; + private: + std::string errorFile; + std::string errorFunc; + int errorLine; + }; + + namespace Assert + { + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + void assertTrue (bool condition , const char* file, const char* func, int line); + void assertFalse (bool condition , const char* file, const char* func, int line); + void assertNull (void* obj , const char* file, const char* func, int line); + void assertNotNull(void* obj , const char* file, const char* func, int line); + void fail( 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 fail() fail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/modules/libkcpp/include/kcpp_error.hpp b/modules/libkcpp/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_exception.hpp b/modules/libkcpp/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_throwable.hpp b/modules/libkcpp/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/src/kc_memory.cpp b/modules/libkcpp/src/kc_memory.cpp deleted file mode 100644 index d33bced..0000000 --- a/modules/libkcpp/src/kc_memory.cpp +++ /dev/null @@ -1,1224 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール (C++) -// @copyright 2003 - 2023 Nomura Kei -// - -#include -#include -#include -#include -#include - -// 常に本来の malloc, free を利用するため、KCPP_MEMORY_ENABLED を無効化する。 -#ifdef KCPP_MEMORY_ENABLED -#undef KCPP_MEMORY_ENABLED -#endif -#include - - -using namespace kcpp; -namespace kcpp -{ - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryListener - // - - /** - * MemoryListener を構築します。 - */ - MemoryListener::MemoryListener() { - // NOP - } - - - /** - * MemoryListener を破棄します。 - */ - MemoryListener::~MemoryListener() - { - // NOP - } - - - /** - * メモリ確保時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyAllocate([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * メモリ解放時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyFree([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * エラー発生時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyError([[maybe_unused]] const MemoryEntry& entry, [[maybe_unused]] const char* msg) - { - // NOP - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntry ユーティリティ (initMemoryEntry) - // - - /** - * メモリエントリに指定されたパラメータを設定、初期化します。 - * - * @param entry 初期化設定するメモリエントリ - * @param size 確保サイズ - * @param mark 確保メモリ状態 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ - void initMemoryEntry(MemoryEntry* entry, - std::size_t size, MemoryMark 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->_prev = nullptr; - entry->_next = nullptr; - entry->data = (entry + 1); - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntryManager (内部クラス) - // メモリのエントリを管理します。 - // - - /** - * メモリエントリを管理するクラス。 - */ - class MemoryEntryManager - { - public: - static constexpr int MAX_BUFFER_SIZE = 256; - - MemoryEntryManager(); - virtual ~MemoryEntryManager(); - void add(MemoryEntry* entry); - void remove(MemoryEntry* entry); - void entries(bool (*handler)(const MemoryEntry& entry)); - void freeif(bool (*handler)(const MemoryEntry& entry)); - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn); - private: - void dumpBinary(std::ostream& stream, void* data, size_t size, int limit); - void dumpAscii(std::ostream& stream, void* data, size_t size, int limit); - const char* toPaddingString(const char* str, int limit); - const char* toHexString(unsigned char data); - MemoryEntryManager(const MemoryEntryManager& mgr) = delete; - MemoryEntryManager& operator=(const MemoryEntryManager& mgr) = delete; - MemoryEntry head; - MemoryEntry tail; - std::recursive_mutex entryMutex; - char tmpbuf[MAX_BUFFER_SIZE]; - }; - - - /** - * MemoryEntryManager を構築します。 - */ - MemoryEntryManager::MemoryEntryManager() : - head { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - tail { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - entryMutex(), - tmpbuf{ 0 } - { - initMemoryEntry(&head, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - initMemoryEntry(&tail, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - head._prev = head._next = &tail; - tail._prev = tail._next = &head; - } - - - /** - * MemoryEntryManager を破棄します。 - */ - MemoryEntryManager::~MemoryEntryManager() - { - // NOP - } - - - /** - * 指定されたメモリエントリを追加します。 - * - * @param entry 追加するメモリエントリ - */ - void MemoryEntryManager::add(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // [tail] の一つ前に挿入する - entry->_next = &tail; - entry->_prev = tail._prev; - tail._prev->_next = entry; - tail._prev = entry; - } - - - /** - * 指定されたメモリエントリを削除します。 - * - * @param entry 削除するメモリエントリ - */ - void MemoryEntryManager::remove(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // entry の前後を直接リンクさせる - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::entries(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isContinue = true; - for (MemoryEntry* current = head._next; isContinue && (current != &tail); current = current->_next) - { - isContinue = handler(*current); - } - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::freeif(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isFree = false; - for (MemoryEntry* current = head._next; current != &tail; ) - { - isFree = handler(*current); - current = current->_next; - if (isFree) - { - MemoryManager::free(current->_prev->data); - } - } - } - - - /** - * 管理しているメモリエントリをダンプします。 - * - * @param stream ダンプ出力先ストリーム - * @param width 幅 - * @param isDumpBinary バイナリダンプ - * @param isDumpAscii ASCII ダンプ - * @param maxColumn 最大桁数 - */ - void MemoryEntryManager::dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - std::lock_guard lock(entryMutex); - - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分の最大表示桁数を取得する。 - int infoColumn = maxColumn; - infoColumn -= (isDumpBinary) ? (dumpByte * 3) + 2 : 0; - infoColumn -= (isDumpAscii) ? (dumpByte ) + 3 : 0; - if (infoColumn < 0) - { - infoColumn = 0; - } - - // 管理している全メモリエントリをループ - for (MemoryEntry* current = head._next; current != &tail; current = current->_next) - { - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分出力 - std::stringstream ss; - ss << current->file << ":" << current->line << " (size=" << current->size << ")" - << " [func=" << current->func << "]"; - stream << toPaddingString(ss.str().c_str(), infoColumn); - - // 16進数ダンプ - if (isDumpBinary) - { - stream << " | "; - dumpBinary(stream, current->data, current->size, dumpByte); - } - - // ASCII ダンプ - if (isDumpAscii) - { - stream << " | "; - dumpAscii(stream, current->data, current->size, dumpByte); - } - - stream << std::endl; - } - } - - - /** - * 指定されたデータを指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpBinary(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - if (idx != 0) { stream << " "; } - stream << toHexString(dataPtr[idx]); - } - for (; idx < limit; idx++) - { - if (idx != 0) { stream << " "; } - stream << "--"; - } - } - - - /** - * 指定されたデータを ASCII 文字で、指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpAscii(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - stream << static_cast((((0x20 <= dataPtr[idx]) && (dataPtr[idx] < 0x7F)) ? dataPtr[idx] : '.')); - } - for (; idx < limit; idx++) - { - stream << " "; - } - } - - /** - * 指定された文字列を指定された文字数にパディングします。 - * 指定された limit より文字数が多い場合は、limit まで文字列が切り詰められます。 - * - * @param str 文字列 - * @param limit 文字数 - * @return 制限された文字列 - */ - const char* MemoryEntryManager::toPaddingString(const char* str, int limit) - { - int maxLimit = (limit <= (MAX_BUFFER_SIZE - 1)) ? limit : (MAX_BUFFER_SIZE - 1); - int len = std::strlen(str); - if (len < maxLimit) - { - memcpy(tmpbuf, str, len); - memset((tmpbuf + len), ' ', (maxLimit - len)); - } - else - { - memcpy(tmpbuf, str, maxLimit); - } - tmpbuf[maxLimit] = '\0'; - return tmpbuf; - } - - - /** - * 指定されたデータを16進数文字列に変換します。 - * - * @param data 変換するデータ - * @return 16進数文字列 - */ - const char* MemoryEntryManager::toHexString(unsigned char data) - { - static const char* HEX_STRINGS = "0123456789ABCDEF"; - tmpbuf[0] = HEX_STRINGS[(static_cast(data) >> 4) & 0x0F]; - tmpbuf[1] = HEX_STRINGS[ static_cast(data) & 0x0F]; - tmpbuf[2] = '\0'; - return tmpbuf; - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryManager - // メモリ管理 - // - - namespace MemoryManager - { - namespace - { - // ================================================================= - // 内部定数 - // ================================================================= - int const PADDING = sizeof(void*) * 2; - - // ================================================================= - // 内部関数プロトタイプ宣言 - // ================================================================= - std::new_handler getNewHandler(); - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void* allocate(std::size_t size, std::align_val_t align, MemoryMark mark, const char* file, const char* func, int line); - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void deallocate(void* ptr, MemoryMark expectedMark); - - // reallocate から呼び出される関数 - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateInvalidPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - - // deallocate から呼び出される関数 - void deallocateEntry(MemoryEntry* entry); - - - // ================================================================= - // 内部変数 - // ================================================================= - MemoryListener defaultListener; - MemoryListener* listener = &defaultListener; //!< メモリ管理リスナ - MemoryEntryManager entryMgr; - - } - - - // ================================================================= - // new 実施時の情報を一時保持するための変数 - // ================================================================= - thread_local const char* file; //!< ファイル名 - thread_local const char* func; //!< 関数 - thread_local int line; //!< 行番号 - - - /** - * メモリ確保, 解放, エラー発生時に通知を受信するリスナを登録します。 - * - * @param l 登録するリスナ - */ - void setListener(MemoryListener& l) - { - listener = &l; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void entries(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.entries(handler); - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void freeif(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.freeif(handler); - } - - - /** - * 管理しているメモリエントリ情報をダンプします。 - * - * @param stream ダンプ先ストリーム - * @param dumpByte ダンプするバイト数 - * @param isDumpBinary バイナリをダンプする - * @param isDumpAscii アスキーをダンプする - * @param maxColumn 最大表示桁数 - */ - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - entryMgr.dump(stream, dumpByte, isDumpBinary, isDumpAscii, maxColumn); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* malloc(std::size_t size, const char* file, const char* func, int line) - { - void* ptr = allocate(size, MEMORY_MARK_ALLOCATED, file, func, line); - return ptr; - } - - - /** - * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 - * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* cmalloc(std::size_t nmemb, std::size_t size, const char* file, const char* func, int line) - { - size_t n = nmemb * size; - void* ptr = allocate(n, MEMORY_MARK_ALLOCATED, file, func, line); - if (ptr != nullptr) - { - std::memset(ptr, 0x00, n); - } - return ptr; - } - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* realloc(void* ptr, std::size_t size, const char* file, const char* func, int line) - { - void* nptr = reallocate(ptr, size, MEMORY_MARK_ALLOCATED, file, func, line); - return nptr; - } - - - /** - * 指定されたメモリを解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ - void free(void* ptr) - { - deallocate(ptr, MEMORY_MARK_ALLOCATED); - } - - - namespace - { - // ================================================================= - // 内部関数の実装 - // ================================================================= - - /** - * new_handler を取得します。 - * @return new_handler - */ - std::new_handler getNewHandler() - { - std::new_handler p = std::set_new_handler(0); - std::set_new_handler(p); - return p; - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::malloc(size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param align アライメント - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, std::align_val_t align, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::aligned_alloc(static_cast(align), size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * ポインタが nullptr の場合、allocate を呼び出します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - if (ptr == nullptr) - { - return allocate(size, mark, file, func, line); - } - - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - switch (oldEntry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み -> 通常の allocate と同様とする。 - return allocate(size, mark, file, func,line); - case MEMORY_MARK_ALLOCATED: // 管理されたメモリの realloc - return reallocateManagedPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - default: // 管理外メモリの realloc - return reallocateUnManagedPtr(ptr, size, mark, file, func, line); - } - } - - - /** - * 指定されたポインタの指すメモリ領域を解放します。 - * nullptr の場合何もしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、fee を実行します。 - * - * @param ptr 解放するメモリへのポインタ - * @param expectedMark 期待するメモリ確保情報 - */ - void deallocate(void* ptr, MemoryMark expectedMark) - { - if (ptr == nullptr) - { - return; - } - - MemoryEntry* entry = static_cast(ptr); - entry--; - if (entry->_mark == expectedMark) - { // 期待するメモリ確保情報の場合、そのまま解放する。 - deallocateEntry(entry); - } - else - { // 期待しないメモリ確保情報の場合 - switch (entry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み - // Nothing to do. - break; - case MEMORY_MARK_ALLOCATED: // 管理メモリ - listener->notifyError(*entry, "warning free memory (please use free)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW: // new により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete[])"); - deallocateEntry(entry); - break; - default: // 管理外メモリ - std::free(ptr); - break; - } - } - } - - - /** - * 管理されたメモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - - // メモリのエントリより削除する - entryMgr.remove(oldEntry); - MemoryEntry* entry = static_cast(std::realloc(oldEntry, size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) - { // メモリ確保成功 - // -> 管理領域の情報を更新して、メモリのエントリとして追加する。 - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で 元々の領域 + 追加分 を、 - // ↓管理領域分を確保した先にコピーする - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - MemoryEntry* entry = static_cast(std::realloc(ptr, size + sizeof(MemoryEntry) + PADDING)); - if (entry != NULL) - { // メモリ確保成功 - // memmove で 元々の領域 + 追加分をコピーして、メモリのエントリとして追加する。 - std::memmove((entry + 1), entry, size); - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ(nullptr 固定) - */ - void* reallocateInvalidPtr([[maybe_unused]] void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - errno = EINVAL; - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate (invalid pointer)"); - return nullptr; - } - - - /** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ - void deallocateEntry(MemoryEntry* entry) - { - listener->notifyFree(*entry); - entry->_mark = MEMORY_MARK_DELETED; - entry->size = 0; - entryMgr.remove(entry); - std::free(entry); - } - } - - } - -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// new/delete 演算子のオーバライド -// - -// C++17 (C++1z) 以降の new/delete 演算子 - - -// ============================================================================= -// new 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -// ============================================================================= -// new[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new[] による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - -// ============================================================================= -// delete 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - - -// ============================================================================= -// delete[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete[] により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp new file mode 100644 index 0000000..cfd47c2 --- /dev/null +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -0,0 +1,386 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Assert +// + +#include + +#include + +#undef assertEquals +#undef assertTrue +#undef assertFalse +#undef assertNull +#undef assertNotNull +#undef fail + +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; + } + + + 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) + { + const char* msg = (expected) + ? "expected: but was:" + : "expected: but was:"; + throw AssertError(msg, file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), 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 std::string& actual, const char* file, const char* func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), 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 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; + std::string actualStr = actual; + 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) + { + throw AssertError("expected: but was:", file, func, line); + } + } + + + /** + * 指定された 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) + { + throw AssertError("expected: but was:", file, func, line); + } + } + + + /** + * 常に、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_error.cpp b/modules/libkcpp/src/kcpp_error.cpp new file mode 100644 index 0000000..ab116a0 --- /dev/null +++ b/modules/libkcpp/src/kcpp_error.cpp @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// + +#include + +namespace kcpp +{ + + /** + * 最後に発生したエラーメッセージを持つ Error を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + Error::Error(const Error& t) noexcept : Throwable(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const std::string& msg) noexcept : Throwable(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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 new file mode 100644 index 0000000..e96259a --- /dev/null +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + AssertError() noexcept; + AssertError(const AssertError& t) noexcept; + AssertError(const std::string& msg) noexcept; + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + virtual ~AssertError() noexcept; + const std::string& getFile() const noexcept; + const std::string& getFunc() const noexcept; + int getLine() const noexcept; + private: + std::string errorFile; + std::string errorFunc; + int errorLine; + }; + + namespace Assert + { + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + void assertTrue (bool condition , const char* file, const char* func, int line); + void assertFalse (bool condition , const char* file, const char* func, int line); + void assertNull (void* obj , const char* file, const char* func, int line); + void assertNotNull(void* obj , const char* file, const char* func, int line); + void fail( 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 fail() fail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/modules/libkcpp/include/kcpp_error.hpp b/modules/libkcpp/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_exception.hpp b/modules/libkcpp/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_throwable.hpp b/modules/libkcpp/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/src/kc_memory.cpp b/modules/libkcpp/src/kc_memory.cpp deleted file mode 100644 index d33bced..0000000 --- a/modules/libkcpp/src/kc_memory.cpp +++ /dev/null @@ -1,1224 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール (C++) -// @copyright 2003 - 2023 Nomura Kei -// - -#include -#include -#include -#include -#include - -// 常に本来の malloc, free を利用するため、KCPP_MEMORY_ENABLED を無効化する。 -#ifdef KCPP_MEMORY_ENABLED -#undef KCPP_MEMORY_ENABLED -#endif -#include - - -using namespace kcpp; -namespace kcpp -{ - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryListener - // - - /** - * MemoryListener を構築します。 - */ - MemoryListener::MemoryListener() { - // NOP - } - - - /** - * MemoryListener を破棄します。 - */ - MemoryListener::~MemoryListener() - { - // NOP - } - - - /** - * メモリ確保時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyAllocate([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * メモリ解放時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyFree([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * エラー発生時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyError([[maybe_unused]] const MemoryEntry& entry, [[maybe_unused]] const char* msg) - { - // NOP - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntry ユーティリティ (initMemoryEntry) - // - - /** - * メモリエントリに指定されたパラメータを設定、初期化します。 - * - * @param entry 初期化設定するメモリエントリ - * @param size 確保サイズ - * @param mark 確保メモリ状態 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ - void initMemoryEntry(MemoryEntry* entry, - std::size_t size, MemoryMark 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->_prev = nullptr; - entry->_next = nullptr; - entry->data = (entry + 1); - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntryManager (内部クラス) - // メモリのエントリを管理します。 - // - - /** - * メモリエントリを管理するクラス。 - */ - class MemoryEntryManager - { - public: - static constexpr int MAX_BUFFER_SIZE = 256; - - MemoryEntryManager(); - virtual ~MemoryEntryManager(); - void add(MemoryEntry* entry); - void remove(MemoryEntry* entry); - void entries(bool (*handler)(const MemoryEntry& entry)); - void freeif(bool (*handler)(const MemoryEntry& entry)); - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn); - private: - void dumpBinary(std::ostream& stream, void* data, size_t size, int limit); - void dumpAscii(std::ostream& stream, void* data, size_t size, int limit); - const char* toPaddingString(const char* str, int limit); - const char* toHexString(unsigned char data); - MemoryEntryManager(const MemoryEntryManager& mgr) = delete; - MemoryEntryManager& operator=(const MemoryEntryManager& mgr) = delete; - MemoryEntry head; - MemoryEntry tail; - std::recursive_mutex entryMutex; - char tmpbuf[MAX_BUFFER_SIZE]; - }; - - - /** - * MemoryEntryManager を構築します。 - */ - MemoryEntryManager::MemoryEntryManager() : - head { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - tail { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - entryMutex(), - tmpbuf{ 0 } - { - initMemoryEntry(&head, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - initMemoryEntry(&tail, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - head._prev = head._next = &tail; - tail._prev = tail._next = &head; - } - - - /** - * MemoryEntryManager を破棄します。 - */ - MemoryEntryManager::~MemoryEntryManager() - { - // NOP - } - - - /** - * 指定されたメモリエントリを追加します。 - * - * @param entry 追加するメモリエントリ - */ - void MemoryEntryManager::add(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // [tail] の一つ前に挿入する - entry->_next = &tail; - entry->_prev = tail._prev; - tail._prev->_next = entry; - tail._prev = entry; - } - - - /** - * 指定されたメモリエントリを削除します。 - * - * @param entry 削除するメモリエントリ - */ - void MemoryEntryManager::remove(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // entry の前後を直接リンクさせる - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::entries(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isContinue = true; - for (MemoryEntry* current = head._next; isContinue && (current != &tail); current = current->_next) - { - isContinue = handler(*current); - } - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::freeif(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isFree = false; - for (MemoryEntry* current = head._next; current != &tail; ) - { - isFree = handler(*current); - current = current->_next; - if (isFree) - { - MemoryManager::free(current->_prev->data); - } - } - } - - - /** - * 管理しているメモリエントリをダンプします。 - * - * @param stream ダンプ出力先ストリーム - * @param width 幅 - * @param isDumpBinary バイナリダンプ - * @param isDumpAscii ASCII ダンプ - * @param maxColumn 最大桁数 - */ - void MemoryEntryManager::dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - std::lock_guard lock(entryMutex); - - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分の最大表示桁数を取得する。 - int infoColumn = maxColumn; - infoColumn -= (isDumpBinary) ? (dumpByte * 3) + 2 : 0; - infoColumn -= (isDumpAscii) ? (dumpByte ) + 3 : 0; - if (infoColumn < 0) - { - infoColumn = 0; - } - - // 管理している全メモリエントリをループ - for (MemoryEntry* current = head._next; current != &tail; current = current->_next) - { - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分出力 - std::stringstream ss; - ss << current->file << ":" << current->line << " (size=" << current->size << ")" - << " [func=" << current->func << "]"; - stream << toPaddingString(ss.str().c_str(), infoColumn); - - // 16進数ダンプ - if (isDumpBinary) - { - stream << " | "; - dumpBinary(stream, current->data, current->size, dumpByte); - } - - // ASCII ダンプ - if (isDumpAscii) - { - stream << " | "; - dumpAscii(stream, current->data, current->size, dumpByte); - } - - stream << std::endl; - } - } - - - /** - * 指定されたデータを指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpBinary(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - if (idx != 0) { stream << " "; } - stream << toHexString(dataPtr[idx]); - } - for (; idx < limit; idx++) - { - if (idx != 0) { stream << " "; } - stream << "--"; - } - } - - - /** - * 指定されたデータを ASCII 文字で、指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpAscii(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - stream << static_cast((((0x20 <= dataPtr[idx]) && (dataPtr[idx] < 0x7F)) ? dataPtr[idx] : '.')); - } - for (; idx < limit; idx++) - { - stream << " "; - } - } - - /** - * 指定された文字列を指定された文字数にパディングします。 - * 指定された limit より文字数が多い場合は、limit まで文字列が切り詰められます。 - * - * @param str 文字列 - * @param limit 文字数 - * @return 制限された文字列 - */ - const char* MemoryEntryManager::toPaddingString(const char* str, int limit) - { - int maxLimit = (limit <= (MAX_BUFFER_SIZE - 1)) ? limit : (MAX_BUFFER_SIZE - 1); - int len = std::strlen(str); - if (len < maxLimit) - { - memcpy(tmpbuf, str, len); - memset((tmpbuf + len), ' ', (maxLimit - len)); - } - else - { - memcpy(tmpbuf, str, maxLimit); - } - tmpbuf[maxLimit] = '\0'; - return tmpbuf; - } - - - /** - * 指定されたデータを16進数文字列に変換します。 - * - * @param data 変換するデータ - * @return 16進数文字列 - */ - const char* MemoryEntryManager::toHexString(unsigned char data) - { - static const char* HEX_STRINGS = "0123456789ABCDEF"; - tmpbuf[0] = HEX_STRINGS[(static_cast(data) >> 4) & 0x0F]; - tmpbuf[1] = HEX_STRINGS[ static_cast(data) & 0x0F]; - tmpbuf[2] = '\0'; - return tmpbuf; - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryManager - // メモリ管理 - // - - namespace MemoryManager - { - namespace - { - // ================================================================= - // 内部定数 - // ================================================================= - int const PADDING = sizeof(void*) * 2; - - // ================================================================= - // 内部関数プロトタイプ宣言 - // ================================================================= - std::new_handler getNewHandler(); - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void* allocate(std::size_t size, std::align_val_t align, MemoryMark mark, const char* file, const char* func, int line); - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void deallocate(void* ptr, MemoryMark expectedMark); - - // reallocate から呼び出される関数 - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateInvalidPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - - // deallocate から呼び出される関数 - void deallocateEntry(MemoryEntry* entry); - - - // ================================================================= - // 内部変数 - // ================================================================= - MemoryListener defaultListener; - MemoryListener* listener = &defaultListener; //!< メモリ管理リスナ - MemoryEntryManager entryMgr; - - } - - - // ================================================================= - // new 実施時の情報を一時保持するための変数 - // ================================================================= - thread_local const char* file; //!< ファイル名 - thread_local const char* func; //!< 関数 - thread_local int line; //!< 行番号 - - - /** - * メモリ確保, 解放, エラー発生時に通知を受信するリスナを登録します。 - * - * @param l 登録するリスナ - */ - void setListener(MemoryListener& l) - { - listener = &l; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void entries(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.entries(handler); - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void freeif(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.freeif(handler); - } - - - /** - * 管理しているメモリエントリ情報をダンプします。 - * - * @param stream ダンプ先ストリーム - * @param dumpByte ダンプするバイト数 - * @param isDumpBinary バイナリをダンプする - * @param isDumpAscii アスキーをダンプする - * @param maxColumn 最大表示桁数 - */ - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - entryMgr.dump(stream, dumpByte, isDumpBinary, isDumpAscii, maxColumn); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* malloc(std::size_t size, const char* file, const char* func, int line) - { - void* ptr = allocate(size, MEMORY_MARK_ALLOCATED, file, func, line); - return ptr; - } - - - /** - * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 - * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* cmalloc(std::size_t nmemb, std::size_t size, const char* file, const char* func, int line) - { - size_t n = nmemb * size; - void* ptr = allocate(n, MEMORY_MARK_ALLOCATED, file, func, line); - if (ptr != nullptr) - { - std::memset(ptr, 0x00, n); - } - return ptr; - } - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* realloc(void* ptr, std::size_t size, const char* file, const char* func, int line) - { - void* nptr = reallocate(ptr, size, MEMORY_MARK_ALLOCATED, file, func, line); - return nptr; - } - - - /** - * 指定されたメモリを解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ - void free(void* ptr) - { - deallocate(ptr, MEMORY_MARK_ALLOCATED); - } - - - namespace - { - // ================================================================= - // 内部関数の実装 - // ================================================================= - - /** - * new_handler を取得します。 - * @return new_handler - */ - std::new_handler getNewHandler() - { - std::new_handler p = std::set_new_handler(0); - std::set_new_handler(p); - return p; - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::malloc(size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param align アライメント - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, std::align_val_t align, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::aligned_alloc(static_cast(align), size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * ポインタが nullptr の場合、allocate を呼び出します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - if (ptr == nullptr) - { - return allocate(size, mark, file, func, line); - } - - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - switch (oldEntry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み -> 通常の allocate と同様とする。 - return allocate(size, mark, file, func,line); - case MEMORY_MARK_ALLOCATED: // 管理されたメモリの realloc - return reallocateManagedPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - default: // 管理外メモリの realloc - return reallocateUnManagedPtr(ptr, size, mark, file, func, line); - } - } - - - /** - * 指定されたポインタの指すメモリ領域を解放します。 - * nullptr の場合何もしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、fee を実行します。 - * - * @param ptr 解放するメモリへのポインタ - * @param expectedMark 期待するメモリ確保情報 - */ - void deallocate(void* ptr, MemoryMark expectedMark) - { - if (ptr == nullptr) - { - return; - } - - MemoryEntry* entry = static_cast(ptr); - entry--; - if (entry->_mark == expectedMark) - { // 期待するメモリ確保情報の場合、そのまま解放する。 - deallocateEntry(entry); - } - else - { // 期待しないメモリ確保情報の場合 - switch (entry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み - // Nothing to do. - break; - case MEMORY_MARK_ALLOCATED: // 管理メモリ - listener->notifyError(*entry, "warning free memory (please use free)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW: // new により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete[])"); - deallocateEntry(entry); - break; - default: // 管理外メモリ - std::free(ptr); - break; - } - } - } - - - /** - * 管理されたメモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - - // メモリのエントリより削除する - entryMgr.remove(oldEntry); - MemoryEntry* entry = static_cast(std::realloc(oldEntry, size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) - { // メモリ確保成功 - // -> 管理領域の情報を更新して、メモリのエントリとして追加する。 - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で 元々の領域 + 追加分 を、 - // ↓管理領域分を確保した先にコピーする - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - MemoryEntry* entry = static_cast(std::realloc(ptr, size + sizeof(MemoryEntry) + PADDING)); - if (entry != NULL) - { // メモリ確保成功 - // memmove で 元々の領域 + 追加分をコピーして、メモリのエントリとして追加する。 - std::memmove((entry + 1), entry, size); - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ(nullptr 固定) - */ - void* reallocateInvalidPtr([[maybe_unused]] void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - errno = EINVAL; - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate (invalid pointer)"); - return nullptr; - } - - - /** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ - void deallocateEntry(MemoryEntry* entry) - { - listener->notifyFree(*entry); - entry->_mark = MEMORY_MARK_DELETED; - entry->size = 0; - entryMgr.remove(entry); - std::free(entry); - } - } - - } - -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// new/delete 演算子のオーバライド -// - -// C++17 (C++1z) 以降の new/delete 演算子 - - -// ============================================================================= -// new 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -// ============================================================================= -// new[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new[] による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - -// ============================================================================= -// delete 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - - -// ============================================================================= -// delete[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete[] により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp new file mode 100644 index 0000000..cfd47c2 --- /dev/null +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -0,0 +1,386 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Assert +// + +#include + +#include + +#undef assertEquals +#undef assertTrue +#undef assertFalse +#undef assertNull +#undef assertNotNull +#undef fail + +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; + } + + + 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) + { + const char* msg = (expected) + ? "expected: but was:" + : "expected: but was:"; + throw AssertError(msg, file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), 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 std::string& actual, const char* file, const char* func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), 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 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; + std::string actualStr = actual; + 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) + { + throw AssertError("expected: but was:", file, func, line); + } + } + + + /** + * 指定された 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) + { + throw AssertError("expected: but was:", file, func, line); + } + } + + + /** + * 常に、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_error.cpp b/modules/libkcpp/src/kcpp_error.cpp new file mode 100644 index 0000000..ab116a0 --- /dev/null +++ b/modules/libkcpp/src/kcpp_error.cpp @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// + +#include + +namespace kcpp +{ + + /** + * 最後に発生したエラーメッセージを持つ Error を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + Error::Error(const Error& t) noexcept : Throwable(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const std::string& msg) noexcept : Throwable(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + +} + diff --git a/modules/libkcpp/src/kcpp_exception.cpp b/modules/libkcpp/src/kcpp_exception.cpp new file mode 100644 index 0000000..b2235d9 --- /dev/null +++ b/modules/libkcpp/src/kcpp_exception.cpp @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// + +#include + +namespace kcpp +{ + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + Exception::Exception(const Exception& t) noexcept : Throwable(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const std::string& msg) noexcept : Throwable(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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 new file mode 100644 index 0000000..e96259a --- /dev/null +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + AssertError() noexcept; + AssertError(const AssertError& t) noexcept; + AssertError(const std::string& msg) noexcept; + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + virtual ~AssertError() noexcept; + const std::string& getFile() const noexcept; + const std::string& getFunc() const noexcept; + int getLine() const noexcept; + private: + std::string errorFile; + std::string errorFunc; + int errorLine; + }; + + namespace Assert + { + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + void assertTrue (bool condition , const char* file, const char* func, int line); + void assertFalse (bool condition , const char* file, const char* func, int line); + void assertNull (void* obj , const char* file, const char* func, int line); + void assertNotNull(void* obj , const char* file, const char* func, int line); + void fail( 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 fail() fail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/modules/libkcpp/include/kcpp_error.hpp b/modules/libkcpp/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_exception.hpp b/modules/libkcpp/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_throwable.hpp b/modules/libkcpp/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/src/kc_memory.cpp b/modules/libkcpp/src/kc_memory.cpp deleted file mode 100644 index d33bced..0000000 --- a/modules/libkcpp/src/kc_memory.cpp +++ /dev/null @@ -1,1224 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール (C++) -// @copyright 2003 - 2023 Nomura Kei -// - -#include -#include -#include -#include -#include - -// 常に本来の malloc, free を利用するため、KCPP_MEMORY_ENABLED を無効化する。 -#ifdef KCPP_MEMORY_ENABLED -#undef KCPP_MEMORY_ENABLED -#endif -#include - - -using namespace kcpp; -namespace kcpp -{ - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryListener - // - - /** - * MemoryListener を構築します。 - */ - MemoryListener::MemoryListener() { - // NOP - } - - - /** - * MemoryListener を破棄します。 - */ - MemoryListener::~MemoryListener() - { - // NOP - } - - - /** - * メモリ確保時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyAllocate([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * メモリ解放時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyFree([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * エラー発生時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyError([[maybe_unused]] const MemoryEntry& entry, [[maybe_unused]] const char* msg) - { - // NOP - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntry ユーティリティ (initMemoryEntry) - // - - /** - * メモリエントリに指定されたパラメータを設定、初期化します。 - * - * @param entry 初期化設定するメモリエントリ - * @param size 確保サイズ - * @param mark 確保メモリ状態 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ - void initMemoryEntry(MemoryEntry* entry, - std::size_t size, MemoryMark 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->_prev = nullptr; - entry->_next = nullptr; - entry->data = (entry + 1); - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntryManager (内部クラス) - // メモリのエントリを管理します。 - // - - /** - * メモリエントリを管理するクラス。 - */ - class MemoryEntryManager - { - public: - static constexpr int MAX_BUFFER_SIZE = 256; - - MemoryEntryManager(); - virtual ~MemoryEntryManager(); - void add(MemoryEntry* entry); - void remove(MemoryEntry* entry); - void entries(bool (*handler)(const MemoryEntry& entry)); - void freeif(bool (*handler)(const MemoryEntry& entry)); - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn); - private: - void dumpBinary(std::ostream& stream, void* data, size_t size, int limit); - void dumpAscii(std::ostream& stream, void* data, size_t size, int limit); - const char* toPaddingString(const char* str, int limit); - const char* toHexString(unsigned char data); - MemoryEntryManager(const MemoryEntryManager& mgr) = delete; - MemoryEntryManager& operator=(const MemoryEntryManager& mgr) = delete; - MemoryEntry head; - MemoryEntry tail; - std::recursive_mutex entryMutex; - char tmpbuf[MAX_BUFFER_SIZE]; - }; - - - /** - * MemoryEntryManager を構築します。 - */ - MemoryEntryManager::MemoryEntryManager() : - head { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - tail { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - entryMutex(), - tmpbuf{ 0 } - { - initMemoryEntry(&head, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - initMemoryEntry(&tail, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - head._prev = head._next = &tail; - tail._prev = tail._next = &head; - } - - - /** - * MemoryEntryManager を破棄します。 - */ - MemoryEntryManager::~MemoryEntryManager() - { - // NOP - } - - - /** - * 指定されたメモリエントリを追加します。 - * - * @param entry 追加するメモリエントリ - */ - void MemoryEntryManager::add(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // [tail] の一つ前に挿入する - entry->_next = &tail; - entry->_prev = tail._prev; - tail._prev->_next = entry; - tail._prev = entry; - } - - - /** - * 指定されたメモリエントリを削除します。 - * - * @param entry 削除するメモリエントリ - */ - void MemoryEntryManager::remove(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // entry の前後を直接リンクさせる - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::entries(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isContinue = true; - for (MemoryEntry* current = head._next; isContinue && (current != &tail); current = current->_next) - { - isContinue = handler(*current); - } - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::freeif(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isFree = false; - for (MemoryEntry* current = head._next; current != &tail; ) - { - isFree = handler(*current); - current = current->_next; - if (isFree) - { - MemoryManager::free(current->_prev->data); - } - } - } - - - /** - * 管理しているメモリエントリをダンプします。 - * - * @param stream ダンプ出力先ストリーム - * @param width 幅 - * @param isDumpBinary バイナリダンプ - * @param isDumpAscii ASCII ダンプ - * @param maxColumn 最大桁数 - */ - void MemoryEntryManager::dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - std::lock_guard lock(entryMutex); - - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分の最大表示桁数を取得する。 - int infoColumn = maxColumn; - infoColumn -= (isDumpBinary) ? (dumpByte * 3) + 2 : 0; - infoColumn -= (isDumpAscii) ? (dumpByte ) + 3 : 0; - if (infoColumn < 0) - { - infoColumn = 0; - } - - // 管理している全メモリエントリをループ - for (MemoryEntry* current = head._next; current != &tail; current = current->_next) - { - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分出力 - std::stringstream ss; - ss << current->file << ":" << current->line << " (size=" << current->size << ")" - << " [func=" << current->func << "]"; - stream << toPaddingString(ss.str().c_str(), infoColumn); - - // 16進数ダンプ - if (isDumpBinary) - { - stream << " | "; - dumpBinary(stream, current->data, current->size, dumpByte); - } - - // ASCII ダンプ - if (isDumpAscii) - { - stream << " | "; - dumpAscii(stream, current->data, current->size, dumpByte); - } - - stream << std::endl; - } - } - - - /** - * 指定されたデータを指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpBinary(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - if (idx != 0) { stream << " "; } - stream << toHexString(dataPtr[idx]); - } - for (; idx < limit; idx++) - { - if (idx != 0) { stream << " "; } - stream << "--"; - } - } - - - /** - * 指定されたデータを ASCII 文字で、指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpAscii(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - stream << static_cast((((0x20 <= dataPtr[idx]) && (dataPtr[idx] < 0x7F)) ? dataPtr[idx] : '.')); - } - for (; idx < limit; idx++) - { - stream << " "; - } - } - - /** - * 指定された文字列を指定された文字数にパディングします。 - * 指定された limit より文字数が多い場合は、limit まで文字列が切り詰められます。 - * - * @param str 文字列 - * @param limit 文字数 - * @return 制限された文字列 - */ - const char* MemoryEntryManager::toPaddingString(const char* str, int limit) - { - int maxLimit = (limit <= (MAX_BUFFER_SIZE - 1)) ? limit : (MAX_BUFFER_SIZE - 1); - int len = std::strlen(str); - if (len < maxLimit) - { - memcpy(tmpbuf, str, len); - memset((tmpbuf + len), ' ', (maxLimit - len)); - } - else - { - memcpy(tmpbuf, str, maxLimit); - } - tmpbuf[maxLimit] = '\0'; - return tmpbuf; - } - - - /** - * 指定されたデータを16進数文字列に変換します。 - * - * @param data 変換するデータ - * @return 16進数文字列 - */ - const char* MemoryEntryManager::toHexString(unsigned char data) - { - static const char* HEX_STRINGS = "0123456789ABCDEF"; - tmpbuf[0] = HEX_STRINGS[(static_cast(data) >> 4) & 0x0F]; - tmpbuf[1] = HEX_STRINGS[ static_cast(data) & 0x0F]; - tmpbuf[2] = '\0'; - return tmpbuf; - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryManager - // メモリ管理 - // - - namespace MemoryManager - { - namespace - { - // ================================================================= - // 内部定数 - // ================================================================= - int const PADDING = sizeof(void*) * 2; - - // ================================================================= - // 内部関数プロトタイプ宣言 - // ================================================================= - std::new_handler getNewHandler(); - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void* allocate(std::size_t size, std::align_val_t align, MemoryMark mark, const char* file, const char* func, int line); - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void deallocate(void* ptr, MemoryMark expectedMark); - - // reallocate から呼び出される関数 - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateInvalidPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - - // deallocate から呼び出される関数 - void deallocateEntry(MemoryEntry* entry); - - - // ================================================================= - // 内部変数 - // ================================================================= - MemoryListener defaultListener; - MemoryListener* listener = &defaultListener; //!< メモリ管理リスナ - MemoryEntryManager entryMgr; - - } - - - // ================================================================= - // new 実施時の情報を一時保持するための変数 - // ================================================================= - thread_local const char* file; //!< ファイル名 - thread_local const char* func; //!< 関数 - thread_local int line; //!< 行番号 - - - /** - * メモリ確保, 解放, エラー発生時に通知を受信するリスナを登録します。 - * - * @param l 登録するリスナ - */ - void setListener(MemoryListener& l) - { - listener = &l; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void entries(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.entries(handler); - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void freeif(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.freeif(handler); - } - - - /** - * 管理しているメモリエントリ情報をダンプします。 - * - * @param stream ダンプ先ストリーム - * @param dumpByte ダンプするバイト数 - * @param isDumpBinary バイナリをダンプする - * @param isDumpAscii アスキーをダンプする - * @param maxColumn 最大表示桁数 - */ - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - entryMgr.dump(stream, dumpByte, isDumpBinary, isDumpAscii, maxColumn); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* malloc(std::size_t size, const char* file, const char* func, int line) - { - void* ptr = allocate(size, MEMORY_MARK_ALLOCATED, file, func, line); - return ptr; - } - - - /** - * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 - * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* cmalloc(std::size_t nmemb, std::size_t size, const char* file, const char* func, int line) - { - size_t n = nmemb * size; - void* ptr = allocate(n, MEMORY_MARK_ALLOCATED, file, func, line); - if (ptr != nullptr) - { - std::memset(ptr, 0x00, n); - } - return ptr; - } - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* realloc(void* ptr, std::size_t size, const char* file, const char* func, int line) - { - void* nptr = reallocate(ptr, size, MEMORY_MARK_ALLOCATED, file, func, line); - return nptr; - } - - - /** - * 指定されたメモリを解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ - void free(void* ptr) - { - deallocate(ptr, MEMORY_MARK_ALLOCATED); - } - - - namespace - { - // ================================================================= - // 内部関数の実装 - // ================================================================= - - /** - * new_handler を取得します。 - * @return new_handler - */ - std::new_handler getNewHandler() - { - std::new_handler p = std::set_new_handler(0); - std::set_new_handler(p); - return p; - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::malloc(size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param align アライメント - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, std::align_val_t align, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::aligned_alloc(static_cast(align), size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * ポインタが nullptr の場合、allocate を呼び出します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - if (ptr == nullptr) - { - return allocate(size, mark, file, func, line); - } - - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - switch (oldEntry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み -> 通常の allocate と同様とする。 - return allocate(size, mark, file, func,line); - case MEMORY_MARK_ALLOCATED: // 管理されたメモリの realloc - return reallocateManagedPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - default: // 管理外メモリの realloc - return reallocateUnManagedPtr(ptr, size, mark, file, func, line); - } - } - - - /** - * 指定されたポインタの指すメモリ領域を解放します。 - * nullptr の場合何もしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、fee を実行します。 - * - * @param ptr 解放するメモリへのポインタ - * @param expectedMark 期待するメモリ確保情報 - */ - void deallocate(void* ptr, MemoryMark expectedMark) - { - if (ptr == nullptr) - { - return; - } - - MemoryEntry* entry = static_cast(ptr); - entry--; - if (entry->_mark == expectedMark) - { // 期待するメモリ確保情報の場合、そのまま解放する。 - deallocateEntry(entry); - } - else - { // 期待しないメモリ確保情報の場合 - switch (entry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み - // Nothing to do. - break; - case MEMORY_MARK_ALLOCATED: // 管理メモリ - listener->notifyError(*entry, "warning free memory (please use free)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW: // new により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete[])"); - deallocateEntry(entry); - break; - default: // 管理外メモリ - std::free(ptr); - break; - } - } - } - - - /** - * 管理されたメモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - - // メモリのエントリより削除する - entryMgr.remove(oldEntry); - MemoryEntry* entry = static_cast(std::realloc(oldEntry, size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) - { // メモリ確保成功 - // -> 管理領域の情報を更新して、メモリのエントリとして追加する。 - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で 元々の領域 + 追加分 を、 - // ↓管理領域分を確保した先にコピーする - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - MemoryEntry* entry = static_cast(std::realloc(ptr, size + sizeof(MemoryEntry) + PADDING)); - if (entry != NULL) - { // メモリ確保成功 - // memmove で 元々の領域 + 追加分をコピーして、メモリのエントリとして追加する。 - std::memmove((entry + 1), entry, size); - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ(nullptr 固定) - */ - void* reallocateInvalidPtr([[maybe_unused]] void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - errno = EINVAL; - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate (invalid pointer)"); - return nullptr; - } - - - /** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ - void deallocateEntry(MemoryEntry* entry) - { - listener->notifyFree(*entry); - entry->_mark = MEMORY_MARK_DELETED; - entry->size = 0; - entryMgr.remove(entry); - std::free(entry); - } - } - - } - -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// new/delete 演算子のオーバライド -// - -// C++17 (C++1z) 以降の new/delete 演算子 - - -// ============================================================================= -// new 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -// ============================================================================= -// new[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new[] による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - -// ============================================================================= -// delete 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - - -// ============================================================================= -// delete[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete[] により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp new file mode 100644 index 0000000..cfd47c2 --- /dev/null +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -0,0 +1,386 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Assert +// + +#include + +#include + +#undef assertEquals +#undef assertTrue +#undef assertFalse +#undef assertNull +#undef assertNotNull +#undef fail + +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; + } + + + 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) + { + const char* msg = (expected) + ? "expected: but was:" + : "expected: but was:"; + throw AssertError(msg, file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), 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 std::string& actual, const char* file, const char* func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), 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 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; + std::string actualStr = actual; + 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) + { + throw AssertError("expected: but was:", file, func, line); + } + } + + + /** + * 指定された 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) + { + throw AssertError("expected: but was:", file, func, line); + } + } + + + /** + * 常に、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_error.cpp b/modules/libkcpp/src/kcpp_error.cpp new file mode 100644 index 0000000..ab116a0 --- /dev/null +++ b/modules/libkcpp/src/kcpp_error.cpp @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// + +#include + +namespace kcpp +{ + + /** + * 最後に発生したエラーメッセージを持つ Error を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + Error::Error(const Error& t) noexcept : Throwable(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const std::string& msg) noexcept : Throwable(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + +} + diff --git a/modules/libkcpp/src/kcpp_exception.cpp b/modules/libkcpp/src/kcpp_exception.cpp new file mode 100644 index 0000000..b2235d9 --- /dev/null +++ b/modules/libkcpp/src/kcpp_exception.cpp @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// + +#include + +namespace kcpp +{ + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + Exception::Exception(const Exception& t) noexcept : Throwable(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const std::string& msg) noexcept : Throwable(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + +} + diff --git a/modules/libkcpp/src/kcpp_memory.cpp b/modules/libkcpp/src/kcpp_memory.cpp new file mode 100644 index 0000000..d33bced --- /dev/null +++ b/modules/libkcpp/src/kcpp_memory.cpp @@ -0,0 +1,1224 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// メモリ管理モジュール (C++) +// @copyright 2003 - 2023 Nomura Kei +// + +#include +#include +#include +#include +#include + +// 常に本来の malloc, free を利用するため、KCPP_MEMORY_ENABLED を無効化する。 +#ifdef KCPP_MEMORY_ENABLED +#undef KCPP_MEMORY_ENABLED +#endif +#include + + +using namespace kcpp; +namespace kcpp +{ + + + //////////////////////////////////////////////////////////////////////////// + // + // MemoryListener + // + + /** + * MemoryListener を構築します。 + */ + MemoryListener::MemoryListener() { + // NOP + } + + + /** + * MemoryListener を破棄します。 + */ + MemoryListener::~MemoryListener() + { + // NOP + } + + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリエントリ(未使用) + */ + void MemoryListener::notifyAllocate([[maybe_unused]] const MemoryEntry& entry) + { + // NOP + } + + + /** + * メモリ解放時に呼び出されます。 + * + * @param entry メモリエントリ(未使用) + */ + void MemoryListener::notifyFree([[maybe_unused]] const MemoryEntry& entry) + { + // NOP + } + + + /** + * エラー発生時に呼び出されます。 + * + * @param entry メモリエントリ(未使用) + */ + void MemoryListener::notifyError([[maybe_unused]] const MemoryEntry& entry, [[maybe_unused]] const char* msg) + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // MemoryEntry ユーティリティ (initMemoryEntry) + // + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, MemoryMark 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->_prev = nullptr; + entry->_next = nullptr; + entry->data = (entry + 1); + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // MemoryEntryManager (内部クラス) + // メモリのエントリを管理します。 + // + + /** + * メモリエントリを管理するクラス。 + */ + class MemoryEntryManager + { + public: + static constexpr int MAX_BUFFER_SIZE = 256; + + MemoryEntryManager(); + virtual ~MemoryEntryManager(); + void add(MemoryEntry* entry); + void remove(MemoryEntry* entry); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn); + private: + void dumpBinary(std::ostream& stream, void* data, size_t size, int limit); + void dumpAscii(std::ostream& stream, void* data, size_t size, int limit); + const char* toPaddingString(const char* str, int limit); + const char* toHexString(unsigned char data); + MemoryEntryManager(const MemoryEntryManager& mgr) = delete; + MemoryEntryManager& operator=(const MemoryEntryManager& mgr) = delete; + MemoryEntry head; + MemoryEntry tail; + std::recursive_mutex entryMutex; + char tmpbuf[MAX_BUFFER_SIZE]; + }; + + + /** + * MemoryEntryManager を構築します。 + */ + MemoryEntryManager::MemoryEntryManager() : + head { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, + tail { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, + entryMutex(), + tmpbuf{ 0 } + { + initMemoryEntry(&head, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); + initMemoryEntry(&tail, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); + head._prev = head._next = &tail; + tail._prev = tail._next = &head; + } + + + /** + * MemoryEntryManager を破棄します。 + */ + MemoryEntryManager::~MemoryEntryManager() + { + // NOP + } + + + /** + * 指定されたメモリエントリを追加します。 + * + * @param entry 追加するメモリエントリ + */ + void MemoryEntryManager::add(MemoryEntry* entry) + { + std::lock_guard lock(entryMutex); + + // [tail] の一つ前に挿入する + entry->_next = &tail; + entry->_prev = tail._prev; + tail._prev->_next = entry; + tail._prev = entry; + } + + + /** + * 指定されたメモリエントリを削除します。 + * + * @param entry 削除するメモリエントリ + */ + void MemoryEntryManager::remove(MemoryEntry* entry) + { + std::lock_guard lock(entryMutex); + + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + } + + + /** + * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラの戻り値が false の場合、呼び出しを終了します。 + * + * @param handler ハンドラ + */ + void MemoryEntryManager::entries(bool (*handler)(const MemoryEntry& entry)) + { + std::lock_guard lock(entryMutex); + + // 管理している全メモリエントリをループ + // handler が false の場合は、停止する。 + bool isContinue = true; + for (MemoryEntry* current = head._next; isContinue && (current != &tail); current = current->_next) + { + isContinue = handler(*current); + } + } + + + /** + * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラの戻り値が true の場合、該当するメモリを解放します。 + * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 + * + * @param handler ハンドラ + */ + void MemoryEntryManager::freeif(bool (*handler)(const MemoryEntry& entry)) + { + std::lock_guard lock(entryMutex); + + // 管理している全メモリエントリをループ + // handler が false の場合は、停止する。 + bool isFree = false; + for (MemoryEntry* current = head._next; current != &tail; ) + { + isFree = handler(*current); + current = current->_next; + if (isFree) + { + MemoryManager::free(current->_prev->data); + } + } + } + + + /** + * 管理しているメモリエントリをダンプします。 + * + * @param stream ダンプ出力先ストリーム + * @param width 幅 + * @param isDumpBinary バイナリダンプ + * @param isDumpAscii ASCII ダンプ + * @param maxColumn 最大桁数 + */ + void MemoryEntryManager::dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) + { + std::lock_guard lock(entryMutex); + + // ファイル名:行番号 (size=サイズ) [func=関数名] 部分の最大表示桁数を取得する。 + int infoColumn = maxColumn; + infoColumn -= (isDumpBinary) ? (dumpByte * 3) + 2 : 0; + infoColumn -= (isDumpAscii) ? (dumpByte ) + 3 : 0; + if (infoColumn < 0) + { + infoColumn = 0; + } + + // 管理している全メモリエントリをループ + for (MemoryEntry* current = head._next; current != &tail; current = current->_next) + { + // ファイル名:行番号 (size=サイズ) [func=関数名] 部分出力 + std::stringstream ss; + ss << current->file << ":" << current->line << " (size=" << current->size << ")" + << " [func=" << current->func << "]"; + stream << toPaddingString(ss.str().c_str(), infoColumn); + + // 16進数ダンプ + if (isDumpBinary) + { + stream << " | "; + dumpBinary(stream, current->data, current->size, dumpByte); + } + + // ASCII ダンプ + if (isDumpAscii) + { + stream << " | "; + dumpAscii(stream, current->data, current->size, dumpByte); + } + + stream << std::endl; + } + } + + + /** + * 指定されたデータを指定されたストリームにダンプします。 + * + * @param stream 出力先ストリーム + * @param data ダンプするデータのポインタ + * @param size ダンプするデータのサイズ + * @param limit ダンプする最大数 + */ + void MemoryEntryManager::dumpBinary(std::ostream& stream, void* data, size_t size, int limit) + { + unsigned char* dataPtr = static_cast(data); + int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; + int idx = 0; + for (; idx < dataLen; idx++) + { + if (idx != 0) { stream << " "; } + stream << toHexString(dataPtr[idx]); + } + for (; idx < limit; idx++) + { + if (idx != 0) { stream << " "; } + stream << "--"; + } + } + + + /** + * 指定されたデータを ASCII 文字で、指定されたストリームにダンプします。 + * + * @param stream 出力先ストリーム + * @param data ダンプするデータのポインタ + * @param size ダンプするデータのサイズ + * @param limit ダンプする最大数 + */ + void MemoryEntryManager::dumpAscii(std::ostream& stream, void* data, size_t size, int limit) + { + unsigned char* dataPtr = static_cast(data); + int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; + int idx = 0; + for (; idx < dataLen; idx++) + { + stream << static_cast((((0x20 <= dataPtr[idx]) && (dataPtr[idx] < 0x7F)) ? dataPtr[idx] : '.')); + } + for (; idx < limit; idx++) + { + stream << " "; + } + } + + /** + * 指定された文字列を指定された文字数にパディングします。 + * 指定された limit より文字数が多い場合は、limit まで文字列が切り詰められます。 + * + * @param str 文字列 + * @param limit 文字数 + * @return 制限された文字列 + */ + const char* MemoryEntryManager::toPaddingString(const char* str, int limit) + { + int maxLimit = (limit <= (MAX_BUFFER_SIZE - 1)) ? limit : (MAX_BUFFER_SIZE - 1); + int len = std::strlen(str); + if (len < maxLimit) + { + memcpy(tmpbuf, str, len); + memset((tmpbuf + len), ' ', (maxLimit - len)); + } + else + { + memcpy(tmpbuf, str, maxLimit); + } + tmpbuf[maxLimit] = '\0'; + return tmpbuf; + } + + + /** + * 指定されたデータを16進数文字列に変換します。 + * + * @param data 変換するデータ + * @return 16進数文字列 + */ + const char* MemoryEntryManager::toHexString(unsigned char data) + { + static const char* HEX_STRINGS = "0123456789ABCDEF"; + tmpbuf[0] = HEX_STRINGS[(static_cast(data) >> 4) & 0x0F]; + tmpbuf[1] = HEX_STRINGS[ static_cast(data) & 0x0F]; + tmpbuf[2] = '\0'; + return tmpbuf; + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // MemoryManager + // メモリ管理 + // + + namespace MemoryManager + { + namespace + { + // ================================================================= + // 内部定数 + // ================================================================= + int const PADDING = sizeof(void*) * 2; + + // ================================================================= + // 内部関数プロトタイプ宣言 + // ================================================================= + std::new_handler getNewHandler(); + void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line); + void* allocate(std::size_t size, std::align_val_t align, MemoryMark mark, const char* file, const char* func, int line); + void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line); + void deallocate(void* ptr, MemoryMark expectedMark); + + // reallocate から呼び出される関数 + void* reallocateManagedPtr(void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line); + void* reallocateUnManagedPtr(void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line); + void* reallocateInvalidPtr(void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line); + + // deallocate から呼び出される関数 + void deallocateEntry(MemoryEntry* entry); + + + // ================================================================= + // 内部変数 + // ================================================================= + MemoryListener defaultListener; + MemoryListener* listener = &defaultListener; //!< メモリ管理リスナ + MemoryEntryManager entryMgr; + + } + + + // ================================================================= + // new 実施時の情報を一時保持するための変数 + // ================================================================= + thread_local const char* file; //!< ファイル名 + thread_local const char* func; //!< 関数 + thread_local int line; //!< 行番号 + + + /** + * メモリ確保, 解放, エラー発生時に通知を受信するリスナを登録します。 + * + * @param l 登録するリスナ + */ + void setListener(MemoryListener& l) + { + listener = &l; + } + + + /** + * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラの戻り値が false の場合、呼び出しを終了します。 + * + * @param handler ハンドラ + */ + void entries(bool (*handler)(const MemoryEntry& entry)) + { + entryMgr.entries(handler); + } + + + /** + * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラの戻り値が true の場合、該当するメモリを解放します。 + * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 + * + * @param handler ハンドラ + */ + void freeif(bool (*handler)(const MemoryEntry& entry)) + { + entryMgr.freeif(handler); + } + + + /** + * 管理しているメモリエントリ情報をダンプします。 + * + * @param stream ダンプ先ストリーム + * @param dumpByte ダンプするバイト数 + * @param isDumpBinary バイナリをダンプする + * @param isDumpAscii アスキーをダンプする + * @param maxColumn 最大表示桁数 + */ + void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) + { + entryMgr.dump(stream, dumpByte, isDumpBinary, isDumpAscii, maxColumn); + } + + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* malloc(std::size_t size, const char* file, const char* func, int line) + { + void* ptr = allocate(size, MEMORY_MARK_ALLOCATED, file, func, line); + return ptr; + } + + + /** + * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 + * + * @param nmemb 確保する要素数 + * @param size 1要素のメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* cmalloc(std::size_t nmemb, std::size_t size, const char* file, const char* func, int line) + { + size_t n = nmemb * size; + void* ptr = allocate(n, MEMORY_MARK_ALLOCATED, file, func, line); + if (ptr != nullptr) + { + std::memset(ptr, 0x00, n); + } + return ptr; + } + + + /** + * 指定されたポインタが指すメモリサイズを変更します。 + * + * @param ptr メモリサイズを変更するポインタ + * @param size 変更後のメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* realloc(void* ptr, std::size_t size, const char* file, const char* func, int line) + { + void* nptr = reallocate(ptr, size, MEMORY_MARK_ALLOCATED, file, func, line); + return nptr; + } + + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void free(void* ptr) + { + deallocate(ptr, MEMORY_MARK_ALLOCATED); + } + + + namespace + { + // ================================================================= + // 内部関数の実装 + // ================================================================= + + /** + * new_handler を取得します。 + * @return new_handler + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line) + { + MemoryEntry* entry; + + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + entry = static_cast(std::malloc(size + sizeof(MemoryEntry) + PADDING)); + if (entry != nullptr) { break; } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { // メモリ確保失敗 + MemoryEntry errorEntry; + initMemoryEntry(&errorEntry, size, mark, file, func, line); + listener->notifyError(errorEntry, "can't allocate"); + return nullptr; + } + } + + initMemoryEntry(entry, size, mark, file, func, line); + entryMgr.add(entry); + listener->notifyAllocate(*entry); + return (entry->data); + } + + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param align アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* allocate(std::size_t size, std::align_val_t align, + MemoryMark mark, const char* file, const char* func, int line) + { + MemoryEntry* entry; + + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + entry = static_cast(std::aligned_alloc(static_cast(align), size + sizeof(MemoryEntry) + PADDING)); + if (entry != nullptr) { break; } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { // メモリ確保失敗 + MemoryEntry errorEntry; + initMemoryEntry(&errorEntry, size, mark, file, func, line); + listener->notifyError(errorEntry, "can't allocate"); + return nullptr; + } + } + + initMemoryEntry(entry, size, mark, file, func, line); + entryMgr.add(entry); + listener->notifyAllocate(*entry); + return (entry->data); + } + + + + /** + * 指定されたポインタが指すメモリサイズを変更します。 + * ポインタが nullptr の場合、allocate を呼び出します。 + * + * @param ptr メモリサイズを変更するポインタ + * @param size 変更後のメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line) + { + if (ptr == nullptr) + { + return allocate(size, mark, file, func, line); + } + + MemoryEntry* oldEntry = static_cast(ptr); + oldEntry--; + switch (oldEntry->_mark) + { + case MEMORY_MARK_DELETED: // 削除済み -> 通常の allocate と同様とする。 + return allocate(size, mark, file, func,line); + case MEMORY_MARK_ALLOCATED: // 管理されたメモリの realloc + return reallocateManagedPtr(ptr, size, mark, file, func, line); + case MEMORY_MARK_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) + return reallocateInvalidPtr(ptr, size, mark, file, func, line); + case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) + return reallocateInvalidPtr(ptr, size, mark, file, func, line); + default: // 管理外メモリの realloc + return reallocateUnManagedPtr(ptr, size, mark, file, func, line); + } + } + + + /** + * 指定されたポインタの指すメモリ領域を解放します。 + * nullptr の場合何もしません。 + * 管理されたメモリの場合、管理領域を合わせて解放します。 + * 管理外メモリの場合、fee を実行します。 + * + * @param ptr 解放するメモリへのポインタ + * @param expectedMark 期待するメモリ確保情報 + */ + void deallocate(void* ptr, MemoryMark expectedMark) + { + if (ptr == nullptr) + { + return; + } + + MemoryEntry* entry = static_cast(ptr); + entry--; + if (entry->_mark == expectedMark) + { // 期待するメモリ確保情報の場合、そのまま解放する。 + deallocateEntry(entry); + } + else + { // 期待しないメモリ確保情報の場合 + switch (entry->_mark) + { + case MEMORY_MARK_DELETED: // 削除済み + // Nothing to do. + break; + case MEMORY_MARK_ALLOCATED: // 管理メモリ + listener->notifyError(*entry, "warning free memory (please use free)"); + deallocateEntry(entry); + break; + case MEMORY_MARK_ALLOCATED_NEW: // new により確保されたメモリ + listener->notifyError(*entry, "warning free memory (please use delete)"); + deallocateEntry(entry); + break; + case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ + listener->notifyError(*entry, "warning free memory (please use delete[])"); + deallocateEntry(entry); + break; + default: // 管理外メモリ + std::free(ptr); + break; + } + } + } + + + /** + * 管理されたメモリ領域に対する realloc を実施します。 + * + * @param ptr メモリサイズを変更するポインタ + * @param size 変更後のメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* reallocateManagedPtr(void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line) + { + MemoryEntry* oldEntry = static_cast(ptr); + oldEntry--; + + // メモリのエントリより削除する + entryMgr.remove(oldEntry); + MemoryEntry* entry = static_cast(std::realloc(oldEntry, size + sizeof(MemoryEntry) + PADDING)); + if (entry != nullptr) + { // メモリ確保成功 + // -> 管理領域の情報を更新して、メモリのエントリとして追加する。 + initMemoryEntry(entry, size, mark, file, func, line); + entryMgr.add(entry); + listener->notifyAllocate(*entry); + return (entry->data); + } + else + { // メモリ確保失敗 + // エラーハンドラを実行して nullptr を返す。 + MemoryEntry errorEntry; + initMemoryEntry(&errorEntry, size, mark, file, func, line); + listener->notifyError(errorEntry, "can't reallocate"); + return nullptr; + } + } + + + /** + * 管理外メモリ領域に対する realloc を実施します。 + * + * @param ptr メモリサイズを変更するポインタ + * @param size 変更後のメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* reallocateUnManagedPtr(void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line) + { + // |<-- 新たな領域 ---------------->| + // +------------+-------------------+ + // | 元々の領域 | 追加分 + 管理領域 | + // +------------+-------------------+ + // ↓ + // ↓memmove で 元々の領域 + 追加分 を、 + // ↓管理領域分を確保した先にコピーする + // ↓ + // +----------+------------+--------+ + // | 管理領域 | 元々の領域 | 追加分 | + // +----------+------------+--------+ + MemoryEntry* entry = static_cast(std::realloc(ptr, size + sizeof(MemoryEntry) + PADDING)); + if (entry != NULL) + { // メモリ確保成功 + // memmove で 元々の領域 + 追加分をコピーして、メモリのエントリとして追加する。 + std::memmove((entry + 1), entry, size); + initMemoryEntry(entry, size, mark, file, func, line); + entryMgr.add(entry); + listener->notifyAllocate(*entry); + return (entry->data); + } + else + { // メモリ確保失敗 + // エラーハンドラを実行して nullptr を返す。 + MemoryEntry errorEntry; + initMemoryEntry(&errorEntry, size, mark, file, func, line); + listener->notifyError(errorEntry, "can't reallocate"); + return nullptr; + } + } + + + /** + * 不正なメモリ領域に対する realloc のエラー処理を実施します。 + * + * @param ptr メモリサイズを変更するポインタ + * @param size 変更後のメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ(nullptr 固定) + */ + void* reallocateInvalidPtr([[maybe_unused]] void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line) + { + errno = EINVAL; + MemoryEntry errorEntry; + initMemoryEntry(&errorEntry, size, mark, file, func, line); + listener->notifyError(errorEntry, "can't reallocate (invalid pointer)"); + return nullptr; + } + + + /** + * 指定されたメモリ管理およびデータ領域を解放します。 + * + * @param entry 解放するメモリ管理領域へのポインタ + */ + void deallocateEntry(MemoryEntry* entry) + { + listener->notifyFree(*entry); + entry->_mark = MEMORY_MARK_DELETED; + entry->size = 0; + entryMgr.remove(entry); + std::free(entry); + } + } + + } + +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// new/delete 演算子のオーバライド +// + +// C++17 (C++1z) 以降の new/delete 演算子 + + +// ============================================================================= +// new 演算子 +// ============================================================================= +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 + +/** + * new による記憶域確保。 + * + * @param size 確保するメモリサイズ + */ +NODISCARD void* operator new(std::size_t size) +{ + void* p = MemoryManager::allocate( + size, + MEMORY_MARK_ALLOCATED_NEW, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + + +/** + * new による例外送出なしでの記憶域確保。 + * + * @param size 確保するメモリサイズ + */ +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept +{ + void* p = MemoryManager::allocate( + size, + MEMORY_MARK_ALLOCATED_NEW, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + return p; +} + + +/** + * new によるデフォルトより大きいアライメント要求の記憶域確保。 + * + * @param size 確保するメモリサイズ + * @param alignment アライメント + */ +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment) +{ + void* p = MemoryManager::allocate( + size, + alignment, + MEMORY_MARK_ALLOCATED_NEW, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + + +/** + * new によるデフォルトより大きいアライメント要求の記憶域確保。 + * + * @param size 確保するメモリサイズ + * @param alignment アライメント + */ +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept +{ + void* p = MemoryManager::allocate( + size, + alignment, + MEMORY_MARK_ALLOCATED_NEW, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + return p; +} + + +// ============================================================================= +// new[] 演算子 +// ============================================================================= +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 + +/** + * new[] による記憶域確保。 + * + * @param size 確保するメモリサイズ + */ +NODISCARD void* operator new[](std::size_t size) +{ + void* p = MemoryManager::allocate( + size, + MEMORY_MARK_ALLOCATED_NEW_ARRAY, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + + +/** + * new[] による例外送出なしでの記憶域確保。 + * + * @param size 確保するメモリサイズ + */ +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept +{ + void* p = MemoryManager::allocate( + size, + MEMORY_MARK_ALLOCATED_NEW_ARRAY, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + return p; +} + + +/** + * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 + * + * @param size 確保するメモリサイズ + * @param alignment アライメント + */ +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment) +{ + void* p = MemoryManager::allocate( + size, + alignment, + MEMORY_MARK_ALLOCATED_NEW_ARRAY, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + + +/** + * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 + * + * @param size 確保するメモリサイズ + * @param alignment アライメント + */ +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept +{ + void* p = MemoryManager::allocate( + size, + alignment, + MEMORY_MARK_ALLOCATED_NEW_ARRAY, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + return p; +} + +// ============================================================================= +// delete 演算子 +// ============================================================================= +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 + + +/** + * delete により、記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ +void operator delete(void* ptr) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + +/** + * delete により、例外送出なしで確保された記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ +void operator delete(void* ptr, const std::nothrow_t&) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + +/** + * delete により、デフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + +/** + * delete により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + +/** + * delete により、オブジェクトサイズが判明している記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param size サイズ + */ +void operator delete(void* ptr, [[maybe_unused]] std::size_t size) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + +/** + * delete により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete(void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + + +// ============================================================================= +// delete[] 演算子 +// ============================================================================= +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 + + +/** + * delete[] により、記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ +void operator delete[](void* ptr) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + + +/** + * delete[] により、例外送出なしで確保された記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ +void operator delete[](void* ptr, const std::nothrow_t&) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + + +/** + * delete[] により、デフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + + +/** + * delete[] により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + + +/** + * delete[] により、オブジェクトサイズが判明している記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param size サイズ + */ +void operator delete[](void* ptr, [[maybe_unused]] std::size_t size) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + + +/** + * delete[] により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete[](void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + diff --git a/modules/libkcpp/include/kcpp.hpp b/modules/libkcpp/include/kcpp.hpp index f4048ef..0f0723c 100644 --- a/modules/libkcpp/include/kcpp.hpp +++ b/modules/libkcpp/include/kcpp.hpp @@ -22,4 +22,52 @@ #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 new file mode 100644 index 0000000..e96259a --- /dev/null +++ b/modules/libkcpp/include/kcpp_assert.hpp @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// KCPP Assert Header File +// +#ifndef KCPP_ASSERT_HPP +#define KCPP_ASSERT_HPP + +#include + +#include + +namespace kcpp +{ + + /** + * アサーションに失敗した際に、throw される Error です。 + */ + class AssertError : public Error + { + public: + AssertError() noexcept; + AssertError(const AssertError& t) noexcept; + AssertError(const std::string& msg) noexcept; + AssertError(const std::string& msg, const char* file, const char* func, int line) noexcept; + virtual ~AssertError() noexcept; + const std::string& getFile() const noexcept; + const std::string& getFunc() const noexcept; + int getLine() const noexcept; + private: + std::string errorFile; + std::string errorFunc; + int errorLine; + }; + + namespace Assert + { + void assertEquals (bool expected, bool actual, const char* file, const char* func, int line); + void assertEquals (char expected, char actual, const char* file, const char* func, int line); + void assertEquals (int expected, int actual, const char* file, const char* func, int line); + void assertEquals (long expected, long actual, const char* file, const char* func, int line); + void assertEquals (double expected, double actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const std::string& actual, const char* file, const char* func, int line); + void assertEquals (const std::string& expected, const char* actual, const char* file, const char* func, int line); + void assertEquals (const char* expected, const char* actual, const char* file, const char* func, int line); + void assertTrue (bool condition , const char* file, const char* func, int line); + void assertFalse (bool condition , const char* file, const char* func, int line); + void assertNull (void* obj , const char* file, const char* func, int line); + void assertNotNull(void* obj , const char* file, const char* func, int line); + void fail( 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 fail() fail(__FILE__, __func__, __LINE__) + } + +} + + +#endif // KCPP_ASSERT_HPP diff --git a/modules/libkcpp/include/kcpp_error.hpp b/modules/libkcpp/include/kcpp_error.hpp new file mode 100644 index 0000000..ea78562 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_exception.hpp b/modules/libkcpp/include/kcpp_exception.hpp new file mode 100644 index 0000000..4f85329 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/include/kcpp_throwable.hpp b/modules/libkcpp/include/kcpp_throwable.hpp new file mode 100644 index 0000000..549e433 --- /dev/null +++ b/modules/libkcpp/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/modules/libkcpp/src/kc_memory.cpp b/modules/libkcpp/src/kc_memory.cpp deleted file mode 100644 index d33bced..0000000 --- a/modules/libkcpp/src/kc_memory.cpp +++ /dev/null @@ -1,1224 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// -// メモリ管理モジュール (C++) -// @copyright 2003 - 2023 Nomura Kei -// - -#include -#include -#include -#include -#include - -// 常に本来の malloc, free を利用するため、KCPP_MEMORY_ENABLED を無効化する。 -#ifdef KCPP_MEMORY_ENABLED -#undef KCPP_MEMORY_ENABLED -#endif -#include - - -using namespace kcpp; -namespace kcpp -{ - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryListener - // - - /** - * MemoryListener を構築します。 - */ - MemoryListener::MemoryListener() { - // NOP - } - - - /** - * MemoryListener を破棄します。 - */ - MemoryListener::~MemoryListener() - { - // NOP - } - - - /** - * メモリ確保時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyAllocate([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * メモリ解放時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyFree([[maybe_unused]] const MemoryEntry& entry) - { - // NOP - } - - - /** - * エラー発生時に呼び出されます。 - * - * @param entry メモリエントリ(未使用) - */ - void MemoryListener::notifyError([[maybe_unused]] const MemoryEntry& entry, [[maybe_unused]] const char* msg) - { - // NOP - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntry ユーティリティ (initMemoryEntry) - // - - /** - * メモリエントリに指定されたパラメータを設定、初期化します。 - * - * @param entry 初期化設定するメモリエントリ - * @param size 確保サイズ - * @param mark 確保メモリ状態 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - */ - void initMemoryEntry(MemoryEntry* entry, - std::size_t size, MemoryMark 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->_prev = nullptr; - entry->_next = nullptr; - entry->data = (entry + 1); - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryEntryManager (内部クラス) - // メモリのエントリを管理します。 - // - - /** - * メモリエントリを管理するクラス。 - */ - class MemoryEntryManager - { - public: - static constexpr int MAX_BUFFER_SIZE = 256; - - MemoryEntryManager(); - virtual ~MemoryEntryManager(); - void add(MemoryEntry* entry); - void remove(MemoryEntry* entry); - void entries(bool (*handler)(const MemoryEntry& entry)); - void freeif(bool (*handler)(const MemoryEntry& entry)); - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn); - private: - void dumpBinary(std::ostream& stream, void* data, size_t size, int limit); - void dumpAscii(std::ostream& stream, void* data, size_t size, int limit); - const char* toPaddingString(const char* str, int limit); - const char* toHexString(unsigned char data); - MemoryEntryManager(const MemoryEntryManager& mgr) = delete; - MemoryEntryManager& operator=(const MemoryEntryManager& mgr) = delete; - MemoryEntry head; - MemoryEntry tail; - std::recursive_mutex entryMutex; - char tmpbuf[MAX_BUFFER_SIZE]; - }; - - - /** - * MemoryEntryManager を構築します。 - */ - MemoryEntryManager::MemoryEntryManager() : - head { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - tail { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, - entryMutex(), - tmpbuf{ 0 } - { - initMemoryEntry(&head, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - initMemoryEntry(&tail, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); - head._prev = head._next = &tail; - tail._prev = tail._next = &head; - } - - - /** - * MemoryEntryManager を破棄します。 - */ - MemoryEntryManager::~MemoryEntryManager() - { - // NOP - } - - - /** - * 指定されたメモリエントリを追加します。 - * - * @param entry 追加するメモリエントリ - */ - void MemoryEntryManager::add(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // [tail] の一つ前に挿入する - entry->_next = &tail; - entry->_prev = tail._prev; - tail._prev->_next = entry; - tail._prev = entry; - } - - - /** - * 指定されたメモリエントリを削除します。 - * - * @param entry 削除するメモリエントリ - */ - void MemoryEntryManager::remove(MemoryEntry* entry) - { - std::lock_guard lock(entryMutex); - - // entry の前後を直接リンクさせる - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::entries(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isContinue = true; - for (MemoryEntry* current = head._next; isContinue && (current != &tail); current = current->_next) - { - isContinue = handler(*current); - } - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void MemoryEntryManager::freeif(bool (*handler)(const MemoryEntry& entry)) - { - std::lock_guard lock(entryMutex); - - // 管理している全メモリエントリをループ - // handler が false の場合は、停止する。 - bool isFree = false; - for (MemoryEntry* current = head._next; current != &tail; ) - { - isFree = handler(*current); - current = current->_next; - if (isFree) - { - MemoryManager::free(current->_prev->data); - } - } - } - - - /** - * 管理しているメモリエントリをダンプします。 - * - * @param stream ダンプ出力先ストリーム - * @param width 幅 - * @param isDumpBinary バイナリダンプ - * @param isDumpAscii ASCII ダンプ - * @param maxColumn 最大桁数 - */ - void MemoryEntryManager::dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - std::lock_guard lock(entryMutex); - - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分の最大表示桁数を取得する。 - int infoColumn = maxColumn; - infoColumn -= (isDumpBinary) ? (dumpByte * 3) + 2 : 0; - infoColumn -= (isDumpAscii) ? (dumpByte ) + 3 : 0; - if (infoColumn < 0) - { - infoColumn = 0; - } - - // 管理している全メモリエントリをループ - for (MemoryEntry* current = head._next; current != &tail; current = current->_next) - { - // ファイル名:行番号 (size=サイズ) [func=関数名] 部分出力 - std::stringstream ss; - ss << current->file << ":" << current->line << " (size=" << current->size << ")" - << " [func=" << current->func << "]"; - stream << toPaddingString(ss.str().c_str(), infoColumn); - - // 16進数ダンプ - if (isDumpBinary) - { - stream << " | "; - dumpBinary(stream, current->data, current->size, dumpByte); - } - - // ASCII ダンプ - if (isDumpAscii) - { - stream << " | "; - dumpAscii(stream, current->data, current->size, dumpByte); - } - - stream << std::endl; - } - } - - - /** - * 指定されたデータを指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpBinary(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - if (idx != 0) { stream << " "; } - stream << toHexString(dataPtr[idx]); - } - for (; idx < limit; idx++) - { - if (idx != 0) { stream << " "; } - stream << "--"; - } - } - - - /** - * 指定されたデータを ASCII 文字で、指定されたストリームにダンプします。 - * - * @param stream 出力先ストリーム - * @param data ダンプするデータのポインタ - * @param size ダンプするデータのサイズ - * @param limit ダンプする最大数 - */ - void MemoryEntryManager::dumpAscii(std::ostream& stream, void* data, size_t size, int limit) - { - unsigned char* dataPtr = static_cast(data); - int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; - int idx = 0; - for (; idx < dataLen; idx++) - { - stream << static_cast((((0x20 <= dataPtr[idx]) && (dataPtr[idx] < 0x7F)) ? dataPtr[idx] : '.')); - } - for (; idx < limit; idx++) - { - stream << " "; - } - } - - /** - * 指定された文字列を指定された文字数にパディングします。 - * 指定された limit より文字数が多い場合は、limit まで文字列が切り詰められます。 - * - * @param str 文字列 - * @param limit 文字数 - * @return 制限された文字列 - */ - const char* MemoryEntryManager::toPaddingString(const char* str, int limit) - { - int maxLimit = (limit <= (MAX_BUFFER_SIZE - 1)) ? limit : (MAX_BUFFER_SIZE - 1); - int len = std::strlen(str); - if (len < maxLimit) - { - memcpy(tmpbuf, str, len); - memset((tmpbuf + len), ' ', (maxLimit - len)); - } - else - { - memcpy(tmpbuf, str, maxLimit); - } - tmpbuf[maxLimit] = '\0'; - return tmpbuf; - } - - - /** - * 指定されたデータを16進数文字列に変換します。 - * - * @param data 変換するデータ - * @return 16進数文字列 - */ - const char* MemoryEntryManager::toHexString(unsigned char data) - { - static const char* HEX_STRINGS = "0123456789ABCDEF"; - tmpbuf[0] = HEX_STRINGS[(static_cast(data) >> 4) & 0x0F]; - tmpbuf[1] = HEX_STRINGS[ static_cast(data) & 0x0F]; - tmpbuf[2] = '\0'; - return tmpbuf; - } - - - - //////////////////////////////////////////////////////////////////////////// - // - // MemoryManager - // メモリ管理 - // - - namespace MemoryManager - { - namespace - { - // ================================================================= - // 内部定数 - // ================================================================= - int const PADDING = sizeof(void*) * 2; - - // ================================================================= - // 内部関数プロトタイプ宣言 - // ================================================================= - std::new_handler getNewHandler(); - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void* allocate(std::size_t size, std::align_val_t align, MemoryMark mark, const char* file, const char* func, int line); - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line); - void deallocate(void* ptr, MemoryMark expectedMark); - - // reallocate から呼び出される関数 - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - void* reallocateInvalidPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line); - - // deallocate から呼び出される関数 - void deallocateEntry(MemoryEntry* entry); - - - // ================================================================= - // 内部変数 - // ================================================================= - MemoryListener defaultListener; - MemoryListener* listener = &defaultListener; //!< メモリ管理リスナ - MemoryEntryManager entryMgr; - - } - - - // ================================================================= - // new 実施時の情報を一時保持するための変数 - // ================================================================= - thread_local const char* file; //!< ファイル名 - thread_local const char* func; //!< 関数 - thread_local int line; //!< 行番号 - - - /** - * メモリ確保, 解放, エラー発生時に通知を受信するリスナを登録します。 - * - * @param l 登録するリスナ - */ - void setListener(MemoryListener& l) - { - listener = &l; - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が false の場合、呼び出しを終了します。 - * - * @param handler ハンドラ - */ - void entries(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.entries(handler); - } - - - /** - * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 - * ハンドラの戻り値が true の場合、該当するメモリを解放します。 - * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 - * - * @param handler ハンドラ - */ - void freeif(bool (*handler)(const MemoryEntry& entry)) - { - entryMgr.freeif(handler); - } - - - /** - * 管理しているメモリエントリ情報をダンプします。 - * - * @param stream ダンプ先ストリーム - * @param dumpByte ダンプするバイト数 - * @param isDumpBinary バイナリをダンプする - * @param isDumpAscii アスキーをダンプする - * @param maxColumn 最大表示桁数 - */ - void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) - { - entryMgr.dump(stream, dumpByte, isDumpBinary, isDumpAscii, maxColumn); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* malloc(std::size_t size, const char* file, const char* func, int line) - { - void* ptr = allocate(size, MEMORY_MARK_ALLOCATED, file, func, line); - return ptr; - } - - - /** - * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 - * - * @param nmemb 確保する要素数 - * @param size 1要素のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* cmalloc(std::size_t nmemb, std::size_t size, const char* file, const char* func, int line) - { - size_t n = nmemb * size; - void* ptr = allocate(n, MEMORY_MARK_ALLOCATED, file, func, line); - if (ptr != nullptr) - { - std::memset(ptr, 0x00, n); - } - return ptr; - } - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* realloc(void* ptr, std::size_t size, const char* file, const char* func, int line) - { - void* nptr = reallocate(ptr, size, MEMORY_MARK_ALLOCATED, file, func, line); - return nptr; - } - - - /** - * 指定されたメモリを解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ - void free(void* ptr) - { - deallocate(ptr, MEMORY_MARK_ALLOCATED); - } - - - namespace - { - // ================================================================= - // 内部関数の実装 - // ================================================================= - - /** - * new_handler を取得します。 - * @return new_handler - */ - std::new_handler getNewHandler() - { - std::new_handler p = std::set_new_handler(0); - std::set_new_handler(p); - return p; - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::malloc(size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - /** - * 指定されたサイズのメモリを確保します。 - * - * @param align アライメント - * @param size 確保するメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* allocate(std::size_t size, std::align_val_t align, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* entry; - - // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] - for (;;) - { - entry = static_cast(std::aligned_alloc(static_cast(align), size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) { break; } - - if (std::new_handler nhandler = getNewHandler()) - { - nhandler(); - } - else - { // メモリ確保失敗 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't allocate"); - return nullptr; - } - } - - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - - - - /** - * 指定されたポインタが指すメモリサイズを変更します。 - * ポインタが nullptr の場合、allocate を呼び出します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line) - { - if (ptr == nullptr) - { - return allocate(size, mark, file, func, line); - } - - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - switch (oldEntry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み -> 通常の allocate と同様とする。 - return allocate(size, mark, file, func,line); - case MEMORY_MARK_ALLOCATED: // 管理されたメモリの realloc - return reallocateManagedPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) - return reallocateInvalidPtr(ptr, size, mark, file, func, line); - default: // 管理外メモリの realloc - return reallocateUnManagedPtr(ptr, size, mark, file, func, line); - } - } - - - /** - * 指定されたポインタの指すメモリ領域を解放します。 - * nullptr の場合何もしません。 - * 管理されたメモリの場合、管理領域を合わせて解放します。 - * 管理外メモリの場合、fee を実行します。 - * - * @param ptr 解放するメモリへのポインタ - * @param expectedMark 期待するメモリ確保情報 - */ - void deallocate(void* ptr, MemoryMark expectedMark) - { - if (ptr == nullptr) - { - return; - } - - MemoryEntry* entry = static_cast(ptr); - entry--; - if (entry->_mark == expectedMark) - { // 期待するメモリ確保情報の場合、そのまま解放する。 - deallocateEntry(entry); - } - else - { // 期待しないメモリ確保情報の場合 - switch (entry->_mark) - { - case MEMORY_MARK_DELETED: // 削除済み - // Nothing to do. - break; - case MEMORY_MARK_ALLOCATED: // 管理メモリ - listener->notifyError(*entry, "warning free memory (please use free)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW: // new により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete)"); - deallocateEntry(entry); - break; - case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ - listener->notifyError(*entry, "warning free memory (please use delete[])"); - deallocateEntry(entry); - break; - default: // 管理外メモリ - std::free(ptr); - break; - } - } - } - - - /** - * 管理されたメモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - MemoryEntry* oldEntry = static_cast(ptr); - oldEntry--; - - // メモリのエントリより削除する - entryMgr.remove(oldEntry); - MemoryEntry* entry = static_cast(std::realloc(oldEntry, size + sizeof(MemoryEntry) + PADDING)); - if (entry != nullptr) - { // メモリ確保成功 - // -> 管理領域の情報を更新して、メモリのエントリとして追加する。 - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ - void* reallocateUnManagedPtr(void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - // |<-- 新たな領域 ---------------->| - // +------------+-------------------+ - // | 元々の領域 | 追加分 + 管理領域 | - // +------------+-------------------+ - // ↓ - // ↓memmove で 元々の領域 + 追加分 を、 - // ↓管理領域分を確保した先にコピーする - // ↓ - // +----------+------------+--------+ - // | 管理領域 | 元々の領域 | 追加分 | - // +----------+------------+--------+ - MemoryEntry* entry = static_cast(std::realloc(ptr, size + sizeof(MemoryEntry) + PADDING)); - if (entry != NULL) - { // メモリ確保成功 - // memmove で 元々の領域 + 追加分をコピーして、メモリのエントリとして追加する。 - std::memmove((entry + 1), entry, size); - initMemoryEntry(entry, size, mark, file, func, line); - entryMgr.add(entry); - listener->notifyAllocate(*entry); - return (entry->data); - } - else - { // メモリ確保失敗 - // エラーハンドラを実行して nullptr を返す。 - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate"); - return nullptr; - } - } - - - /** - * 不正なメモリ領域に対する realloc のエラー処理を実施します。 - * - * @param ptr メモリサイズを変更するポインタ - * @param size 変更後のメモリサイズ - * @param mark メモリ確保情報 - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ(nullptr 固定) - */ - void* reallocateInvalidPtr([[maybe_unused]] void* ptr, std::size_t size, - MemoryMark mark, const char* file, const char* func, int line) - { - errno = EINVAL; - MemoryEntry errorEntry; - initMemoryEntry(&errorEntry, size, mark, file, func, line); - listener->notifyError(errorEntry, "can't reallocate (invalid pointer)"); - return nullptr; - } - - - /** - * 指定されたメモリ管理およびデータ領域を解放します。 - * - * @param entry 解放するメモリ管理領域へのポインタ - */ - void deallocateEntry(MemoryEntry* entry) - { - listener->notifyFree(*entry); - entry->_mark = MEMORY_MARK_DELETED; - entry->size = 0; - entryMgr.remove(entry); - std::free(entry); - } - } - - } - -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// new/delete 演算子のオーバライド -// - -// C++17 (C++1z) 以降の new/delete 演算子 - - -// ============================================================================= -// new 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -// ============================================================================= -// new[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域確保 -// (2) 例外送出なしで記憶域確保 -// (3) デフォルトより大きいアライメント要求の記憶域確保 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 - -/** - * new[] による記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size) -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] による例外送出なしでの記憶域確保。 - * - * @param size 確保するメモリサイズ - */ -NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment) -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - if (p == nullptr) - { - throw std::bad_alloc(); - } - return p; -} - - -/** - * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 - * - * @param size 確保するメモリサイズ - * @param alignment アライメント - */ -NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept -{ - void* p = MemoryManager::allocate( - size, - alignment, - MEMORY_MARK_ALLOCATED_NEW_ARRAY, - MemoryManager::file, - MemoryManager::func, - MemoryManager::line); - return p; -} - -// ============================================================================= -// delete 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete(void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - -/** - * delete により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete(void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); -} - - - -// ============================================================================= -// delete[] 演算子 -// ============================================================================= -// 下記、順に -// (1) 記憶域解放 -// (2) 例外送出なしで確保された記憶域解放 -// (3) デフォルトより大きいアライメント要求の記憶域解放 -// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 -// (5) オブジェクトサイズが判明している記憶域解放 -// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 - - -/** - * delete[] により、記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外送出なしで確保された記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - */ -void operator delete[](void* ptr, const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、デフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明している記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param size サイズ - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - - -/** - * delete[] により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 - * - * @param ptr 解放するメモリへのポインタ - * @param alignment アライメント - */ -void operator delete[](void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept -{ - MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); -} - diff --git a/modules/libkcpp/src/kcpp_assert.cpp b/modules/libkcpp/src/kcpp_assert.cpp new file mode 100644 index 0000000..cfd47c2 --- /dev/null +++ b/modules/libkcpp/src/kcpp_assert.cpp @@ -0,0 +1,386 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Assert +// + +#include + +#include + +#undef assertEquals +#undef assertTrue +#undef assertFalse +#undef assertNull +#undef assertNotNull +#undef fail + +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; + } + + + 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) + { + const char* msg = (expected) + ? "expected: but was:" + : "expected: but was:"; + throw AssertError(msg, file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), file, func, line); + } + } + + + /** + * 値を比較します。 + * 通常、本メソッドを直接使用せず、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) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), 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 std::string& actual, const char* file, const char* func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected:<" << expected << "> but was:<" << actual << ">"; + throw AssertError(msg.str(), 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 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; + std::string actualStr = actual; + 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) + { + throw AssertError("expected: but was:", file, func, line); + } + } + + + /** + * 指定された 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) + { + throw AssertError("expected: but was:", file, func, line); + } + } + + + /** + * 常に、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_error.cpp b/modules/libkcpp/src/kcpp_error.cpp new file mode 100644 index 0000000..ab116a0 --- /dev/null +++ b/modules/libkcpp/src/kcpp_error.cpp @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Error +// + +#include + +namespace kcpp +{ + + /** + * 最後に発生したエラーメッセージを持つ Error を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + Error::Error(const Error& t) noexcept : Throwable(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const std::string& msg) noexcept : Throwable(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + +} + diff --git a/modules/libkcpp/src/kcpp_exception.cpp b/modules/libkcpp/src/kcpp_exception.cpp new file mode 100644 index 0000000..b2235d9 --- /dev/null +++ b/modules/libkcpp/src/kcpp_exception.cpp @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Exception +// + +#include + +namespace kcpp +{ + + /** + * 最後に発生したエラーメッセージを持つ Exception を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + Exception::Exception(const Exception& t) noexcept : Throwable(t) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const std::string& msg) noexcept : Throwable(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + +} + diff --git a/modules/libkcpp/src/kcpp_memory.cpp b/modules/libkcpp/src/kcpp_memory.cpp new file mode 100644 index 0000000..d33bced --- /dev/null +++ b/modules/libkcpp/src/kcpp_memory.cpp @@ -0,0 +1,1224 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// メモリ管理モジュール (C++) +// @copyright 2003 - 2023 Nomura Kei +// + +#include +#include +#include +#include +#include + +// 常に本来の malloc, free を利用するため、KCPP_MEMORY_ENABLED を無効化する。 +#ifdef KCPP_MEMORY_ENABLED +#undef KCPP_MEMORY_ENABLED +#endif +#include + + +using namespace kcpp; +namespace kcpp +{ + + + //////////////////////////////////////////////////////////////////////////// + // + // MemoryListener + // + + /** + * MemoryListener を構築します。 + */ + MemoryListener::MemoryListener() { + // NOP + } + + + /** + * MemoryListener を破棄します。 + */ + MemoryListener::~MemoryListener() + { + // NOP + } + + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリエントリ(未使用) + */ + void MemoryListener::notifyAllocate([[maybe_unused]] const MemoryEntry& entry) + { + // NOP + } + + + /** + * メモリ解放時に呼び出されます。 + * + * @param entry メモリエントリ(未使用) + */ + void MemoryListener::notifyFree([[maybe_unused]] const MemoryEntry& entry) + { + // NOP + } + + + /** + * エラー発生時に呼び出されます。 + * + * @param entry メモリエントリ(未使用) + */ + void MemoryListener::notifyError([[maybe_unused]] const MemoryEntry& entry, [[maybe_unused]] const char* msg) + { + // NOP + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // MemoryEntry ユーティリティ (initMemoryEntry) + // + + /** + * メモリエントリに指定されたパラメータを設定、初期化します。 + * + * @param entry 初期化設定するメモリエントリ + * @param size 確保サイズ + * @param mark 確保メモリ状態 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + */ + void initMemoryEntry(MemoryEntry* entry, + std::size_t size, MemoryMark 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->_prev = nullptr; + entry->_next = nullptr; + entry->data = (entry + 1); + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // MemoryEntryManager (内部クラス) + // メモリのエントリを管理します。 + // + + /** + * メモリエントリを管理するクラス。 + */ + class MemoryEntryManager + { + public: + static constexpr int MAX_BUFFER_SIZE = 256; + + MemoryEntryManager(); + virtual ~MemoryEntryManager(); + void add(MemoryEntry* entry); + void remove(MemoryEntry* entry); + void entries(bool (*handler)(const MemoryEntry& entry)); + void freeif(bool (*handler)(const MemoryEntry& entry)); + void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn); + private: + void dumpBinary(std::ostream& stream, void* data, size_t size, int limit); + void dumpAscii(std::ostream& stream, void* data, size_t size, int limit); + const char* toPaddingString(const char* str, int limit); + const char* toHexString(unsigned char data); + MemoryEntryManager(const MemoryEntryManager& mgr) = delete; + MemoryEntryManager& operator=(const MemoryEntryManager& mgr) = delete; + MemoryEntry head; + MemoryEntry tail; + std::recursive_mutex entryMutex; + char tmpbuf[MAX_BUFFER_SIZE]; + }; + + + /** + * MemoryEntryManager を構築します。 + */ + MemoryEntryManager::MemoryEntryManager() : + head { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, + tail { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr }, + entryMutex(), + tmpbuf{ 0 } + { + initMemoryEntry(&head, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); + initMemoryEntry(&tail, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0); + head._prev = head._next = &tail; + tail._prev = tail._next = &head; + } + + + /** + * MemoryEntryManager を破棄します。 + */ + MemoryEntryManager::~MemoryEntryManager() + { + // NOP + } + + + /** + * 指定されたメモリエントリを追加します。 + * + * @param entry 追加するメモリエントリ + */ + void MemoryEntryManager::add(MemoryEntry* entry) + { + std::lock_guard lock(entryMutex); + + // [tail] の一つ前に挿入する + entry->_next = &tail; + entry->_prev = tail._prev; + tail._prev->_next = entry; + tail._prev = entry; + } + + + /** + * 指定されたメモリエントリを削除します。 + * + * @param entry 削除するメモリエントリ + */ + void MemoryEntryManager::remove(MemoryEntry* entry) + { + std::lock_guard lock(entryMutex); + + // entry の前後を直接リンクさせる + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + } + + + /** + * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラの戻り値が false の場合、呼び出しを終了します。 + * + * @param handler ハンドラ + */ + void MemoryEntryManager::entries(bool (*handler)(const MemoryEntry& entry)) + { + std::lock_guard lock(entryMutex); + + // 管理している全メモリエントリをループ + // handler が false の場合は、停止する。 + bool isContinue = true; + for (MemoryEntry* current = head._next; isContinue && (current != &tail); current = current->_next) + { + isContinue = handler(*current); + } + } + + + /** + * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラの戻り値が true の場合、該当するメモリを解放します。 + * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 + * + * @param handler ハンドラ + */ + void MemoryEntryManager::freeif(bool (*handler)(const MemoryEntry& entry)) + { + std::lock_guard lock(entryMutex); + + // 管理している全メモリエントリをループ + // handler が false の場合は、停止する。 + bool isFree = false; + for (MemoryEntry* current = head._next; current != &tail; ) + { + isFree = handler(*current); + current = current->_next; + if (isFree) + { + MemoryManager::free(current->_prev->data); + } + } + } + + + /** + * 管理しているメモリエントリをダンプします。 + * + * @param stream ダンプ出力先ストリーム + * @param width 幅 + * @param isDumpBinary バイナリダンプ + * @param isDumpAscii ASCII ダンプ + * @param maxColumn 最大桁数 + */ + void MemoryEntryManager::dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) + { + std::lock_guard lock(entryMutex); + + // ファイル名:行番号 (size=サイズ) [func=関数名] 部分の最大表示桁数を取得する。 + int infoColumn = maxColumn; + infoColumn -= (isDumpBinary) ? (dumpByte * 3) + 2 : 0; + infoColumn -= (isDumpAscii) ? (dumpByte ) + 3 : 0; + if (infoColumn < 0) + { + infoColumn = 0; + } + + // 管理している全メモリエントリをループ + for (MemoryEntry* current = head._next; current != &tail; current = current->_next) + { + // ファイル名:行番号 (size=サイズ) [func=関数名] 部分出力 + std::stringstream ss; + ss << current->file << ":" << current->line << " (size=" << current->size << ")" + << " [func=" << current->func << "]"; + stream << toPaddingString(ss.str().c_str(), infoColumn); + + // 16進数ダンプ + if (isDumpBinary) + { + stream << " | "; + dumpBinary(stream, current->data, current->size, dumpByte); + } + + // ASCII ダンプ + if (isDumpAscii) + { + stream << " | "; + dumpAscii(stream, current->data, current->size, dumpByte); + } + + stream << std::endl; + } + } + + + /** + * 指定されたデータを指定されたストリームにダンプします。 + * + * @param stream 出力先ストリーム + * @param data ダンプするデータのポインタ + * @param size ダンプするデータのサイズ + * @param limit ダンプする最大数 + */ + void MemoryEntryManager::dumpBinary(std::ostream& stream, void* data, size_t size, int limit) + { + unsigned char* dataPtr = static_cast(data); + int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; + int idx = 0; + for (; idx < dataLen; idx++) + { + if (idx != 0) { stream << " "; } + stream << toHexString(dataPtr[idx]); + } + for (; idx < limit; idx++) + { + if (idx != 0) { stream << " "; } + stream << "--"; + } + } + + + /** + * 指定されたデータを ASCII 文字で、指定されたストリームにダンプします。 + * + * @param stream 出力先ストリーム + * @param data ダンプするデータのポインタ + * @param size ダンプするデータのサイズ + * @param limit ダンプする最大数 + */ + void MemoryEntryManager::dumpAscii(std::ostream& stream, void* data, size_t size, int limit) + { + unsigned char* dataPtr = static_cast(data); + int dataLen = (static_cast(size) < limit) ? static_cast(size) : limit; + int idx = 0; + for (; idx < dataLen; idx++) + { + stream << static_cast((((0x20 <= dataPtr[idx]) && (dataPtr[idx] < 0x7F)) ? dataPtr[idx] : '.')); + } + for (; idx < limit; idx++) + { + stream << " "; + } + } + + /** + * 指定された文字列を指定された文字数にパディングします。 + * 指定された limit より文字数が多い場合は、limit まで文字列が切り詰められます。 + * + * @param str 文字列 + * @param limit 文字数 + * @return 制限された文字列 + */ + const char* MemoryEntryManager::toPaddingString(const char* str, int limit) + { + int maxLimit = (limit <= (MAX_BUFFER_SIZE - 1)) ? limit : (MAX_BUFFER_SIZE - 1); + int len = std::strlen(str); + if (len < maxLimit) + { + memcpy(tmpbuf, str, len); + memset((tmpbuf + len), ' ', (maxLimit - len)); + } + else + { + memcpy(tmpbuf, str, maxLimit); + } + tmpbuf[maxLimit] = '\0'; + return tmpbuf; + } + + + /** + * 指定されたデータを16進数文字列に変換します。 + * + * @param data 変換するデータ + * @return 16進数文字列 + */ + const char* MemoryEntryManager::toHexString(unsigned char data) + { + static const char* HEX_STRINGS = "0123456789ABCDEF"; + tmpbuf[0] = HEX_STRINGS[(static_cast(data) >> 4) & 0x0F]; + tmpbuf[1] = HEX_STRINGS[ static_cast(data) & 0x0F]; + tmpbuf[2] = '\0'; + return tmpbuf; + } + + + + //////////////////////////////////////////////////////////////////////////// + // + // MemoryManager + // メモリ管理 + // + + namespace MemoryManager + { + namespace + { + // ================================================================= + // 内部定数 + // ================================================================= + int const PADDING = sizeof(void*) * 2; + + // ================================================================= + // 内部関数プロトタイプ宣言 + // ================================================================= + std::new_handler getNewHandler(); + void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line); + void* allocate(std::size_t size, std::align_val_t align, MemoryMark mark, const char* file, const char* func, int line); + void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line); + void deallocate(void* ptr, MemoryMark expectedMark); + + // reallocate から呼び出される関数 + void* reallocateManagedPtr(void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line); + void* reallocateUnManagedPtr(void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line); + void* reallocateInvalidPtr(void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line); + + // deallocate から呼び出される関数 + void deallocateEntry(MemoryEntry* entry); + + + // ================================================================= + // 内部変数 + // ================================================================= + MemoryListener defaultListener; + MemoryListener* listener = &defaultListener; //!< メモリ管理リスナ + MemoryEntryManager entryMgr; + + } + + + // ================================================================= + // new 実施時の情報を一時保持するための変数 + // ================================================================= + thread_local const char* file; //!< ファイル名 + thread_local const char* func; //!< 関数 + thread_local int line; //!< 行番号 + + + /** + * メモリ確保, 解放, エラー発生時に通知を受信するリスナを登録します。 + * + * @param l 登録するリスナ + */ + void setListener(MemoryListener& l) + { + listener = &l; + } + + + /** + * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラの戻り値が false の場合、呼び出しを終了します。 + * + * @param handler ハンドラ + */ + void entries(bool (*handler)(const MemoryEntry& entry)) + { + entryMgr.entries(handler); + } + + + /** + * 管理しているメモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラの戻り値が true の場合、該当するメモリを解放します。 + * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。 + * + * @param handler ハンドラ + */ + void freeif(bool (*handler)(const MemoryEntry& entry)) + { + entryMgr.freeif(handler); + } + + + /** + * 管理しているメモリエントリ情報をダンプします。 + * + * @param stream ダンプ先ストリーム + * @param dumpByte ダンプするバイト数 + * @param isDumpBinary バイナリをダンプする + * @param isDumpAscii アスキーをダンプする + * @param maxColumn 最大表示桁数 + */ + void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn) + { + entryMgr.dump(stream, dumpByte, isDumpBinary, isDumpAscii, maxColumn); + } + + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* malloc(std::size_t size, const char* file, const char* func, int line) + { + void* ptr = allocate(size, MEMORY_MARK_ALLOCATED, file, func, line); + return ptr; + } + + + /** + * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 + * + * @param nmemb 確保する要素数 + * @param size 1要素のメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* cmalloc(std::size_t nmemb, std::size_t size, const char* file, const char* func, int line) + { + size_t n = nmemb * size; + void* ptr = allocate(n, MEMORY_MARK_ALLOCATED, file, func, line); + if (ptr != nullptr) + { + std::memset(ptr, 0x00, n); + } + return ptr; + } + + + /** + * 指定されたポインタが指すメモリサイズを変更します。 + * + * @param ptr メモリサイズを変更するポインタ + * @param size 変更後のメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* realloc(void* ptr, std::size_t size, const char* file, const char* func, int line) + { + void* nptr = reallocate(ptr, size, MEMORY_MARK_ALLOCATED, file, func, line); + return nptr; + } + + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void free(void* ptr) + { + deallocate(ptr, MEMORY_MARK_ALLOCATED); + } + + + namespace + { + // ================================================================= + // 内部関数の実装 + // ================================================================= + + /** + * new_handler を取得します。 + * @return new_handler + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line) + { + MemoryEntry* entry; + + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + entry = static_cast(std::malloc(size + sizeof(MemoryEntry) + PADDING)); + if (entry != nullptr) { break; } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { // メモリ確保失敗 + MemoryEntry errorEntry; + initMemoryEntry(&errorEntry, size, mark, file, func, line); + listener->notifyError(errorEntry, "can't allocate"); + return nullptr; + } + } + + initMemoryEntry(entry, size, mark, file, func, line); + entryMgr.add(entry); + listener->notifyAllocate(*entry); + return (entry->data); + } + + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param align アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* allocate(std::size_t size, std::align_val_t align, + MemoryMark mark, const char* file, const char* func, int line) + { + MemoryEntry* entry; + + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + entry = static_cast(std::aligned_alloc(static_cast(align), size + sizeof(MemoryEntry) + PADDING)); + if (entry != nullptr) { break; } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { // メモリ確保失敗 + MemoryEntry errorEntry; + initMemoryEntry(&errorEntry, size, mark, file, func, line); + listener->notifyError(errorEntry, "can't allocate"); + return nullptr; + } + } + + initMemoryEntry(entry, size, mark, file, func, line); + entryMgr.add(entry); + listener->notifyAllocate(*entry); + return (entry->data); + } + + + + /** + * 指定されたポインタが指すメモリサイズを変更します。 + * ポインタが nullptr の場合、allocate を呼び出します。 + * + * @param ptr メモリサイズを変更するポインタ + * @param size 変更後のメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line) + { + if (ptr == nullptr) + { + return allocate(size, mark, file, func, line); + } + + MemoryEntry* oldEntry = static_cast(ptr); + oldEntry--; + switch (oldEntry->_mark) + { + case MEMORY_MARK_DELETED: // 削除済み -> 通常の allocate と同様とする。 + return allocate(size, mark, file, func,line); + case MEMORY_MARK_ALLOCATED: // 管理されたメモリの realloc + return reallocateManagedPtr(ptr, size, mark, file, func, line); + case MEMORY_MARK_ALLOCATED_NEW: // 不正 (new で確保されたメモリへの realloc) + return reallocateInvalidPtr(ptr, size, mark, file, func, line); + case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // 不正 (new[] で確保されたメモリへの realloc) + return reallocateInvalidPtr(ptr, size, mark, file, func, line); + default: // 管理外メモリの realloc + return reallocateUnManagedPtr(ptr, size, mark, file, func, line); + } + } + + + /** + * 指定されたポインタの指すメモリ領域を解放します。 + * nullptr の場合何もしません。 + * 管理されたメモリの場合、管理領域を合わせて解放します。 + * 管理外メモリの場合、fee を実行します。 + * + * @param ptr 解放するメモリへのポインタ + * @param expectedMark 期待するメモリ確保情報 + */ + void deallocate(void* ptr, MemoryMark expectedMark) + { + if (ptr == nullptr) + { + return; + } + + MemoryEntry* entry = static_cast(ptr); + entry--; + if (entry->_mark == expectedMark) + { // 期待するメモリ確保情報の場合、そのまま解放する。 + deallocateEntry(entry); + } + else + { // 期待しないメモリ確保情報の場合 + switch (entry->_mark) + { + case MEMORY_MARK_DELETED: // 削除済み + // Nothing to do. + break; + case MEMORY_MARK_ALLOCATED: // 管理メモリ + listener->notifyError(*entry, "warning free memory (please use free)"); + deallocateEntry(entry); + break; + case MEMORY_MARK_ALLOCATED_NEW: // new により確保されたメモリ + listener->notifyError(*entry, "warning free memory (please use delete)"); + deallocateEntry(entry); + break; + case MEMORY_MARK_ALLOCATED_NEW_ARRAY: // new[] により確保されたメモリ + listener->notifyError(*entry, "warning free memory (please use delete[])"); + deallocateEntry(entry); + break; + default: // 管理外メモリ + std::free(ptr); + break; + } + } + } + + + /** + * 管理されたメモリ領域に対する realloc を実施します。 + * + * @param ptr メモリサイズを変更するポインタ + * @param size 変更後のメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* reallocateManagedPtr(void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line) + { + MemoryEntry* oldEntry = static_cast(ptr); + oldEntry--; + + // メモリのエントリより削除する + entryMgr.remove(oldEntry); + MemoryEntry* entry = static_cast(std::realloc(oldEntry, size + sizeof(MemoryEntry) + PADDING)); + if (entry != nullptr) + { // メモリ確保成功 + // -> 管理領域の情報を更新して、メモリのエントリとして追加する。 + initMemoryEntry(entry, size, mark, file, func, line); + entryMgr.add(entry); + listener->notifyAllocate(*entry); + return (entry->data); + } + else + { // メモリ確保失敗 + // エラーハンドラを実行して nullptr を返す。 + MemoryEntry errorEntry; + initMemoryEntry(&errorEntry, size, mark, file, func, line); + listener->notifyError(errorEntry, "can't reallocate"); + return nullptr; + } + } + + + /** + * 管理外メモリ領域に対する realloc を実施します。 + * + * @param ptr メモリサイズを変更するポインタ + * @param size 変更後のメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void* reallocateUnManagedPtr(void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line) + { + // |<-- 新たな領域 ---------------->| + // +------------+-------------------+ + // | 元々の領域 | 追加分 + 管理領域 | + // +------------+-------------------+ + // ↓ + // ↓memmove で 元々の領域 + 追加分 を、 + // ↓管理領域分を確保した先にコピーする + // ↓ + // +----------+------------+--------+ + // | 管理領域 | 元々の領域 | 追加分 | + // +----------+------------+--------+ + MemoryEntry* entry = static_cast(std::realloc(ptr, size + sizeof(MemoryEntry) + PADDING)); + if (entry != NULL) + { // メモリ確保成功 + // memmove で 元々の領域 + 追加分をコピーして、メモリのエントリとして追加する。 + std::memmove((entry + 1), entry, size); + initMemoryEntry(entry, size, mark, file, func, line); + entryMgr.add(entry); + listener->notifyAllocate(*entry); + return (entry->data); + } + else + { // メモリ確保失敗 + // エラーハンドラを実行して nullptr を返す。 + MemoryEntry errorEntry; + initMemoryEntry(&errorEntry, size, mark, file, func, line); + listener->notifyError(errorEntry, "can't reallocate"); + return nullptr; + } + } + + + /** + * 不正なメモリ領域に対する realloc のエラー処理を実施します。 + * + * @param ptr メモリサイズを変更するポインタ + * @param size 変更後のメモリサイズ + * @param mark メモリ確保情報 + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ(nullptr 固定) + */ + void* reallocateInvalidPtr([[maybe_unused]] void* ptr, std::size_t size, + MemoryMark mark, const char* file, const char* func, int line) + { + errno = EINVAL; + MemoryEntry errorEntry; + initMemoryEntry(&errorEntry, size, mark, file, func, line); + listener->notifyError(errorEntry, "can't reallocate (invalid pointer)"); + return nullptr; + } + + + /** + * 指定されたメモリ管理およびデータ領域を解放します。 + * + * @param entry 解放するメモリ管理領域へのポインタ + */ + void deallocateEntry(MemoryEntry* entry) + { + listener->notifyFree(*entry); + entry->_mark = MEMORY_MARK_DELETED; + entry->size = 0; + entryMgr.remove(entry); + std::free(entry); + } + } + + } + +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// new/delete 演算子のオーバライド +// + +// C++17 (C++1z) 以降の new/delete 演算子 + + +// ============================================================================= +// new 演算子 +// ============================================================================= +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 + +/** + * new による記憶域確保。 + * + * @param size 確保するメモリサイズ + */ +NODISCARD void* operator new(std::size_t size) +{ + void* p = MemoryManager::allocate( + size, + MEMORY_MARK_ALLOCATED_NEW, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + + +/** + * new による例外送出なしでの記憶域確保。 + * + * @param size 確保するメモリサイズ + */ +NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept +{ + void* p = MemoryManager::allocate( + size, + MEMORY_MARK_ALLOCATED_NEW, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + return p; +} + + +/** + * new によるデフォルトより大きいアライメント要求の記憶域確保。 + * + * @param size 確保するメモリサイズ + * @param alignment アライメント + */ +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment) +{ + void* p = MemoryManager::allocate( + size, + alignment, + MEMORY_MARK_ALLOCATED_NEW, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + + +/** + * new によるデフォルトより大きいアライメント要求の記憶域確保。 + * + * @param size 確保するメモリサイズ + * @param alignment アライメント + */ +NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept +{ + void* p = MemoryManager::allocate( + size, + alignment, + MEMORY_MARK_ALLOCATED_NEW, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + return p; +} + + +// ============================================================================= +// new[] 演算子 +// ============================================================================= +// 下記、順に +// (1) 記憶域確保 +// (2) 例外送出なしで記憶域確保 +// (3) デフォルトより大きいアライメント要求の記憶域確保 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 + +/** + * new[] による記憶域確保。 + * + * @param size 確保するメモリサイズ + */ +NODISCARD void* operator new[](std::size_t size) +{ + void* p = MemoryManager::allocate( + size, + MEMORY_MARK_ALLOCATED_NEW_ARRAY, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + + +/** + * new[] による例外送出なしでの記憶域確保。 + * + * @param size 確保するメモリサイズ + */ +NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept +{ + void* p = MemoryManager::allocate( + size, + MEMORY_MARK_ALLOCATED_NEW_ARRAY, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + return p; +} + + +/** + * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 + * + * @param size 確保するメモリサイズ + * @param alignment アライメント + */ +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment) +{ + void* p = MemoryManager::allocate( + size, + alignment, + MEMORY_MARK_ALLOCATED_NEW_ARRAY, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + + +/** + * new[] によるデフォルトより大きいアライメント要求の記憶域確保。 + * + * @param size 確保するメモリサイズ + * @param alignment アライメント + */ +NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept +{ + void* p = MemoryManager::allocate( + size, + alignment, + MEMORY_MARK_ALLOCATED_NEW_ARRAY, + MemoryManager::file, + MemoryManager::func, + MemoryManager::line); + return p; +} + +// ============================================================================= +// delete 演算子 +// ============================================================================= +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 + + +/** + * delete により、記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ +void operator delete(void* ptr) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + +/** + * delete により、例外送出なしで確保された記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ +void operator delete(void* ptr, const std::nothrow_t&) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + +/** + * delete により、デフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + +/** + * delete により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + +/** + * delete により、オブジェクトサイズが判明している記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param size サイズ + */ +void operator delete(void* ptr, [[maybe_unused]] std::size_t size) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + +/** + * delete により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete(void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW); +} + + + +// ============================================================================= +// delete[] 演算子 +// ============================================================================= +// 下記、順に +// (1) 記憶域解放 +// (2) 例外送出なしで確保された記憶域解放 +// (3) デフォルトより大きいアライメント要求の記憶域解放 +// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 +// (5) オブジェクトサイズが判明している記憶域解放 +// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 + + +/** + * delete[] により、記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ +void operator delete[](void* ptr) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + + +/** + * delete[] により、例外送出なしで確保された記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ +void operator delete[](void* ptr, const std::nothrow_t&) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + + +/** + * delete[] により、デフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + + +/** + * delete[] により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + + +/** + * delete[] により、オブジェクトサイズが判明している記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param size サイズ + */ +void operator delete[](void* ptr, [[maybe_unused]] std::size_t size) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + + +/** + * delete[] により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param alignment アライメント + */ +void operator delete[](void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept +{ + MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY); +} + diff --git a/modules/libkcpp/src/kcpp_throwable.cpp b/modules/libkcpp/src/kcpp_throwable.cpp new file mode 100644 index 0000000..45fbcb8 --- /dev/null +++ b/modules/libkcpp/src/kcpp_throwable.cpp @@ -0,0 +1,101 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Throwable +// + +#include +#include + +#include + +#ifndef MAXMSG +#define MAXMSG (256) +#endif + +namespace kcpp +{ + + /** + * 最後に発生したエラーメッセージを持つ Throwable を構築します。 + * エラーメッセージを取得できない場合、空文字がメッセージに設定されます。 + */ + Throwable::Throwable() noexcept : message("") + { +#if (KCPP_IS_WINDOWS) + // Window の場合 + int errnum = GetLastError(); + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER // 動作フラグ + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_TNSERTS, + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR) &lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + if (ret != 0) + { + message = static_cast(lpMsgBuf); + } + localFree(lpMsgBuf); +#elif ((_POSIX_C_SOURCE >= 200112L || __XOPEN_SOURCE >= 600) && ! _GNU_SOURCE) + // XSI準拠 strerror_r が提供されている + char buf[MAXMSG]; + int ret = stderror_r(errno, buf, sizeof(buf)); + if (ret == 0) + { + message = buf; + } +#else + // ANSI 準拠 streror を利用 + char* errMsg = strerror(errno); + message = errMsg; +#endif + } + + + /** + * コピーコンストラクタ。 + * + * @param t コピー元 + */ + Throwable::Throwable(const Throwable& t) noexcept : message(t.message) + { + // NOP + } + + + /** + * 指定されたメッセージを持つ Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const std::string& msg) noexcept : message(msg) + { + // NOP + } + + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + + /** + * エラーメッセージを返します。 + * + * @return エラーメッセージ + */ + const char* Throwable::what() const noexcept + { + return message.c_str(); + } + +} +