Newer
Older
libkc / modules / src / kc_assert.c
/**
 * @file  kc_assert.c
 * @brief アサーションモジュール
 * @copyright  2003 - 2023  Nomura Kei
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include <math.h>
#include <kc_assert.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);
        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;
}