/** * @file kc_assert.c * @brief アサーションモジュール * @copyright 2003 - 2023 Nomura Kei */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <kc_assert.h> #include <kc_threads.h> // プロトタイプ宣言 static void assert_default_handler(const char *msg); static const char *assert_hexdump(char *buf, const void *data, size_t size); // ハンドラ /** * Assertion 発生時に実行されるハンドラ。 * * @param msg エラーメッセージ */ void (*assert_handler)(const char *msg) = assert_default_handler; /** Assertion で利用するメッセージバッファ。 */ static thread_local char assert_message[1024]; /** * 指定された値が一致するか否か検査します。 * * @param expected 期待する値 * @param actual 実際の値 * @param file ファイル * @param func 関数 * @param line 行番号 */ void assert_equals_long_( long expected, long actual, const char *file, const char *func, int line) { bool is_success = (expected == actual); if (!is_success) { snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion expected <%ld> but was <%ld>", file, line, func, expected, actual); assert_handler(assert_message); } } /** * 指定された値が一致するか否か検査します。 * * @param expected 期待する値 * @param actual 実際の値 * @param file ファイル * @param func 関数 * @param line 行番号 */ void assert_equals_double_( double expected, double actual, double delta, const char *file, const char *func, int line) { bool is_success = (fabs((double)expected - actual) < delta); if (!is_success) { snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion expected <%f> but was <%f>", file, line, func, expected, actual); assert_handler(assert_message); } } /** * 指定された文字列が一致するか否か検査します。 * * @param expected 期待する値 * @param actual 実際の値 * @param file ファイル * @param func 関数 * @param line 行番号 */ void assert_equals_string_( const char *expected, const char *actual, const char *file, const char *func, int line) { bool is_success = (strcmp(expected, actual) == 0); if (!is_success) { snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion expected <%s> but was <%s>", file, line, func, expected, actual); if (strlen(actual) == 6) { printf("[%s]\n", actual); printf("[%c][%02x]\n", actual[5], actual[5]); } assert_handler(assert_message); } } /** * 指定されたバイナリが一致するか否か検査します。 * * @param expected 期待する値 * @param actual 実際の値 * @param file ファイル * @param func 関数 * @param line 行番号 */ void assert_equals_binary_( const void *expected, const void *actual, size_t size, const char *file, const char *func, int line) { bool is_success = (memcmp(expected, actual, size) == 0); if (!is_success) { char expected_buf[64]; char actual_buf[64]; const char *expected_dump = assert_hexdump(expected_buf, expected, size); const char *actual_dump = assert_hexdump(actual_buf, actual, size); snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion expected <%s> but was <%s>", file, line, func, expected_dump, actual_dump); assert_handler(assert_message); } } /** * 指定された値がNULLであることを検査します。 * * @param condition 検査する値 * @param file ファイル * @param func 関数 * @param line 行番号 */ void assert_null_(const void *condition, const char *file, const char *func, int line) { bool is_success = (condition == NULL); if (!is_success) { snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion condition is not null", file, line, func); assert_handler(assert_message); } } /** * 指定された値がNULLでないことを検査します。 * * @param condition 検査する値 * @param file ファイル * @param func 関数 * @param line 行番号 */ void assert_not_null_(const void *condition, const char *file, const char *func, int line) { bool is_success = (condition != NULL); if (!is_success) { snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion condition is null", file, line, func); assert_handler(assert_message); } } /** * 指定された値が false であることを検査します。 * * @param condition 検査する値 * @param file ファイル * @param func 関数 * @param line 行番号 */ void assert_false_(bool condition, const char *file, const char *func, int line) { bool is_success = (!condition); if (!is_success) { snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion condition is not false", file, line, func); assert_handler(assert_message); } } /** * 指定された値が true であることを検査します。 * * @param condition 検査する値 * @param file ファイル * @param func 関数 * @param line 行番号 */ void assert_true_(bool condition, const char *file, const char *func, int line) { bool is_success = (condition); if (!is_success) { snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion condition is not true", file, line, func); assert_handler(assert_message); } } /** * 常に失敗であることを示します。 * * @param file ファイル * @param func 関数 * @param line 行番号 */ void assert_fail_(const char *file, const char *func, int line) { snprintf(assert_message, sizeof(assert_message), "%s:%d: %s: Assertion fail()", file, line, func); assert_handler(assert_message); } /** * デフォルトアサーションハンドラ。 * * @param is_success 成功か否か * @param msg メッセージ */ static void assert_default_handler(const char *msg) { fprintf(stderr, "%s\n", msg); abort(); } /** * 最大 16 バイトの16進数ダンプ文字列をバッファに格納します。 * * @param buff 結果を格納するためのバッファ [48 以上のこと] * @param data データ * @param size データのサイズ */ static const char *assert_hexdump(char *buf, const void *data, size_t size) { int dump_size = (size < 16) ? size : 16; const char *ptr = (const char *)data; char *write_ptr = buf; int write_size = 0; for (int i = 0; i < dump_size; i++) { write_size += sprintf(write_ptr, "%02X ", *ptr); write_ptr += write_size; ptr++; } return buf; }