diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // | | <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/modules/test/a-test.gcno b/modules/test/a-test.gcno new file mode 100644 index 0000000..8152c12 --- /dev/null +++ b/modules/test/a-test.gcno Binary files differ diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/modules/test/a-test.gcno b/modules/test/a-test.gcno new file mode 100644 index 0000000..8152c12 --- /dev/null +++ b/modules/test/a-test.gcno Binary files differ diff --git a/modules/test/a.out b/modules/test/a.out new file mode 100755 index 0000000..933cb99 --- /dev/null +++ b/modules/test/a.out Binary files differ diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/modules/test/a-test.gcno b/modules/test/a-test.gcno new file mode 100644 index 0000000..8152c12 --- /dev/null +++ b/modules/test/a-test.gcno Binary files differ diff --git a/modules/test/a.out b/modules/test/a.out new file mode 100755 index 0000000..933cb99 --- /dev/null +++ b/modules/test/a.out Binary files differ diff --git a/modules/test/src/test_dl.c b/modules/test/src/test_dl.c new file mode 100644 index 0000000..de5c0a0 --- /dev/null +++ b/modules/test/src/test_dl.c @@ -0,0 +1,63 @@ + +#include + +#include +#include +#include +#include + +#include "ut.h" + +#if (KC_IS_WINDOWS) +#define FILENAME "test-lib/libtest.dll" +#else +#define FILENAME "test-lib/libtest.so" +#endif + +// プロトタイプ宣言 +static void test_dl(void); + +/** + * KcDl 単体テストスイート + */ +void suite_dl(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "dl", test_dl); +} + +/** + * dl テスト。 + * + * @process 動的ライブラリをロードする。 + * @result ライブラリがロードされること。 + * + * @process ロードしたライブラリの関数を実行する。 + * @result 関数が実行されること。 + */ +static void test_dl(void) +{ + dl_handle_t handle = KcDl_open(FILENAME); + + // add + int (*test_add)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_add"); + int res = test_add(10, 20); + assert_equals(30, res); + + // sub + int (*test_sub)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_sub"); + res = test_sub(10, 20); + assert_equals(-10, res); + + // mul + int (*test_mul)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_mul"); + res = test_mul(10, 20); + assert_equals(200, res); + + // mul + int (*test_div)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_div"); + res = test_div(50, 10); + assert_equals(5, res); + + KcDl_close(handle); +} diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/modules/test/a-test.gcno b/modules/test/a-test.gcno new file mode 100644 index 0000000..8152c12 --- /dev/null +++ b/modules/test/a-test.gcno Binary files differ diff --git a/modules/test/a.out b/modules/test/a.out new file mode 100755 index 0000000..933cb99 --- /dev/null +++ b/modules/test/a.out Binary files differ diff --git a/modules/test/src/test_dl.c b/modules/test/src/test_dl.c new file mode 100644 index 0000000..de5c0a0 --- /dev/null +++ b/modules/test/src/test_dl.c @@ -0,0 +1,63 @@ + +#include + +#include +#include +#include +#include + +#include "ut.h" + +#if (KC_IS_WINDOWS) +#define FILENAME "test-lib/libtest.dll" +#else +#define FILENAME "test-lib/libtest.so" +#endif + +// プロトタイプ宣言 +static void test_dl(void); + +/** + * KcDl 単体テストスイート + */ +void suite_dl(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "dl", test_dl); +} + +/** + * dl テスト。 + * + * @process 動的ライブラリをロードする。 + * @result ライブラリがロードされること。 + * + * @process ロードしたライブラリの関数を実行する。 + * @result 関数が実行されること。 + */ +static void test_dl(void) +{ + dl_handle_t handle = KcDl_open(FILENAME); + + // add + int (*test_add)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_add"); + int res = test_add(10, 20); + assert_equals(30, res); + + // sub + int (*test_sub)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_sub"); + res = test_sub(10, 20); + assert_equals(-10, res); + + // mul + int (*test_mul)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_mul"); + res = test_mul(10, 20); + assert_equals(200, res); + + // mul + int (*test_div)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_div"); + res = test_div(50, 10); + assert_equals(5, res); + + KcDl_close(handle); +} diff --git a/modules/test/src/test_env.c b/modules/test/src/test_env.c new file mode 100644 index 0000000..0321563 --- /dev/null +++ b/modules/test/src/test_env.c @@ -0,0 +1,84 @@ + +#include + +#include +#include +#include +#include + +static void test_env(void); + +/** + * env 単体テストスイート + */ +void suite_env(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "env", test_env); +} + +/** + * env テスト。 + */ +static void test_env(void) +{ + // 事前設定 + KcEnv_remove("UT_TEST_ENV"); + + // 値設定 + bool ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + + // 上書き無し、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", false); + assert_true(ret); + char *env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("ABC", env_val); + + // 上書きあり、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", true); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("XYZ", env_val); + + // 削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // 存在しないキーを削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // キーに = が含まれる。 + ret = KcEnv_set("UT_TEST_ENV=XXX", "ABC", false); + assert_false(ret); + + // = が含まれたキーで取得 + ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV=XXX"); + assert_equals("ABC", env_val); + + // キーの値が 4096 超える + char key[8192] = {'\0'}; + for (int i = 0; i < 5000; i++) + { + key[i] = 'X'; + } + key[4999] = 'Z'; + ret = KcEnv_set(key, "XYZ", false); + assert_true(ret); + + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); + + // = 以降は無視されて値が取得されることを確認 + key[5000] = '='; + key[5001] = 'A'; + key[5002] = 'B'; + key[5003] = 'C'; + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); +} diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/modules/test/a-test.gcno b/modules/test/a-test.gcno new file mode 100644 index 0000000..8152c12 --- /dev/null +++ b/modules/test/a-test.gcno Binary files differ diff --git a/modules/test/a.out b/modules/test/a.out new file mode 100755 index 0000000..933cb99 --- /dev/null +++ b/modules/test/a.out Binary files differ diff --git a/modules/test/src/test_dl.c b/modules/test/src/test_dl.c new file mode 100644 index 0000000..de5c0a0 --- /dev/null +++ b/modules/test/src/test_dl.c @@ -0,0 +1,63 @@ + +#include + +#include +#include +#include +#include + +#include "ut.h" + +#if (KC_IS_WINDOWS) +#define FILENAME "test-lib/libtest.dll" +#else +#define FILENAME "test-lib/libtest.so" +#endif + +// プロトタイプ宣言 +static void test_dl(void); + +/** + * KcDl 単体テストスイート + */ +void suite_dl(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "dl", test_dl); +} + +/** + * dl テスト。 + * + * @process 動的ライブラリをロードする。 + * @result ライブラリがロードされること。 + * + * @process ロードしたライブラリの関数を実行する。 + * @result 関数が実行されること。 + */ +static void test_dl(void) +{ + dl_handle_t handle = KcDl_open(FILENAME); + + // add + int (*test_add)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_add"); + int res = test_add(10, 20); + assert_equals(30, res); + + // sub + int (*test_sub)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_sub"); + res = test_sub(10, 20); + assert_equals(-10, res); + + // mul + int (*test_mul)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_mul"); + res = test_mul(10, 20); + assert_equals(200, res); + + // mul + int (*test_div)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_div"); + res = test_div(50, 10); + assert_equals(5, res); + + KcDl_close(handle); +} diff --git a/modules/test/src/test_env.c b/modules/test/src/test_env.c new file mode 100644 index 0000000..0321563 --- /dev/null +++ b/modules/test/src/test_env.c @@ -0,0 +1,84 @@ + +#include + +#include +#include +#include +#include + +static void test_env(void); + +/** + * env 単体テストスイート + */ +void suite_env(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "env", test_env); +} + +/** + * env テスト。 + */ +static void test_env(void) +{ + // 事前設定 + KcEnv_remove("UT_TEST_ENV"); + + // 値設定 + bool ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + + // 上書き無し、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", false); + assert_true(ret); + char *env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("ABC", env_val); + + // 上書きあり、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", true); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("XYZ", env_val); + + // 削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // 存在しないキーを削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // キーに = が含まれる。 + ret = KcEnv_set("UT_TEST_ENV=XXX", "ABC", false); + assert_false(ret); + + // = が含まれたキーで取得 + ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV=XXX"); + assert_equals("ABC", env_val); + + // キーの値が 4096 超える + char key[8192] = {'\0'}; + for (int i = 0; i < 5000; i++) + { + key[i] = 'X'; + } + key[4999] = 'Z'; + ret = KcEnv_set(key, "XYZ", false); + assert_true(ret); + + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); + + // = 以降は無視されて値が取得されることを確認 + key[5000] = '='; + key[5001] = 'A'; + key[5002] = 'B'; + key[5003] = 'C'; + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); +} diff --git a/modules/test/src/test_map.c b/modules/test/src/test_map.c new file mode 100644 index 0000000..9953336 --- /dev/null +++ b/modules/test/src/test_map.c @@ -0,0 +1,298 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_map_new(void); +static void test_map_put(void); +static void test_map_remove(void); +static void test_map_entries(void); +static void test_map_malloc_error(void); + +/** + * KcMap 単体テストスイート + */ +void suite_map(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "map new/delete", test_map_new); + ut->add(ut, UT_TESTCASE, "map put/get", test_map_put); + ut->add(ut, UT_TESTCASE, "map remove", test_map_remove); + ut->add(ut, UT_TESTCASE, "map entries", test_map_entries); + ut->add(ut, UT_TESTCASE, "map malloc error", test_map_malloc_error); +} + +/** + * Map 生成/破棄。 + * + * @process KcMap_new を実行する。。 + * @result KcMap が生成されること。 + * + * @process KcMap_delete にて Map を破棄する。 + * @result Map が破棄されること。 + */ +static void test_map_new(void) +{ + KcMap *map = KcMap_new(0); + assert_not_null(map); + KcMap_delete(map); + + map = KcMap_new(65538); + assert_not_null(map); + KcMap_delete(map); + + // map にデータが残った状態での削除 + map = KcMap_new(10); + assert_not_null(map); + map->put(map, "key1", "value1", 7); + KcMap_delete(map); +} + +/** + * Map への追加/取得。 + */ +static void test_map_put(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // 値取得 + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + size_t size; + const char *value = (const char *)map->get(map, keys[i], &size); + assert_equals(vals[i], value); + } + + // 存在しないキーの値取得 + void *ptr = map->get(map, "ABC", NULL); + assert_null(ptr); + + // キー上書き登録 + res = (char *)map->put(map, "key10", "abcdefg", strlen("abcdefg") + 1); + assert_equals("abcdefg", (const char *)res); + res = (char *)map->get(map, "key10", NULL); + assert_equals("abcdefg", (const char *)res); + + KcMap_delete(map); +} + +/** + * Map からの削除/サイズ確認。 + */ +static void test_map_remove(void) +{ + KcMap *map = KcMap_new(3); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + int size_counter = 14; + map->remove(map, "key15"); + size_t size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key15", NULL); + assert_null(res); + + map->remove(map, "key7"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key7", NULL); + assert_null(res); + + map->remove(map, "key13"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key13", NULL); + assert_null(res); + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + // 存在しないキー削除 + map->remove(map, "abc"); + size = map->size(map); + assert_equals(9, size); + + KcMap_delete(map); + + // パターン2 + map = KcMap_new(3); + assert_not_null(map); + + size_counter = 3; + for (int i = 0; i < size_counter; i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + size_counter--; + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + KcMap_delete(map); +} + +// エントリ +typedef struct UserData_ +{ + int value; +} UserData; +static bool test_map_entries_handler(const char *key, const void *val, size_t size, void *args) +{ + UserData *user_data = (UserData *)args; + user_data->value--; + printf("key=%-5s, value=%-5s, size=%zu\n", key, (const char *)val, size); + return (user_data->value >= 0); +} + +/** + * Map エントリ取得 + */ +static void test_map_entries(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // entries + UserData args = { + .value = 10000}; + map->entries(map, test_map_entries_handler, &args); + + // entries [途中中断] + UserData args2 = { + .value = 5}; + map->entries(map, test_map_entries_handler, &args2); + + KcMap_delete(map); +} + +/** + * メモリ確保失敗 + */ +static void test_map_malloc_error(void) +{ + // 生成時失敗 + ut_alloc_control(0) + { + KcMap *map = KcMap_new(5); + assert_null(map); + } + + // put 時失敗 + KcMap *map = KcMap_new(5); + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + ut_alloc_control(0) + { + res = (char *)map->put(map, "abc", "def", 4); + assert_null(res); + } + KcMap_delete(map); +} \ No newline at end of file diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/modules/test/a-test.gcno b/modules/test/a-test.gcno new file mode 100644 index 0000000..8152c12 --- /dev/null +++ b/modules/test/a-test.gcno Binary files differ diff --git a/modules/test/a.out b/modules/test/a.out new file mode 100755 index 0000000..933cb99 --- /dev/null +++ b/modules/test/a.out Binary files differ diff --git a/modules/test/src/test_dl.c b/modules/test/src/test_dl.c new file mode 100644 index 0000000..de5c0a0 --- /dev/null +++ b/modules/test/src/test_dl.c @@ -0,0 +1,63 @@ + +#include + +#include +#include +#include +#include + +#include "ut.h" + +#if (KC_IS_WINDOWS) +#define FILENAME "test-lib/libtest.dll" +#else +#define FILENAME "test-lib/libtest.so" +#endif + +// プロトタイプ宣言 +static void test_dl(void); + +/** + * KcDl 単体テストスイート + */ +void suite_dl(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "dl", test_dl); +} + +/** + * dl テスト。 + * + * @process 動的ライブラリをロードする。 + * @result ライブラリがロードされること。 + * + * @process ロードしたライブラリの関数を実行する。 + * @result 関数が実行されること。 + */ +static void test_dl(void) +{ + dl_handle_t handle = KcDl_open(FILENAME); + + // add + int (*test_add)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_add"); + int res = test_add(10, 20); + assert_equals(30, res); + + // sub + int (*test_sub)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_sub"); + res = test_sub(10, 20); + assert_equals(-10, res); + + // mul + int (*test_mul)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_mul"); + res = test_mul(10, 20); + assert_equals(200, res); + + // mul + int (*test_div)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_div"); + res = test_div(50, 10); + assert_equals(5, res); + + KcDl_close(handle); +} diff --git a/modules/test/src/test_env.c b/modules/test/src/test_env.c new file mode 100644 index 0000000..0321563 --- /dev/null +++ b/modules/test/src/test_env.c @@ -0,0 +1,84 @@ + +#include + +#include +#include +#include +#include + +static void test_env(void); + +/** + * env 単体テストスイート + */ +void suite_env(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "env", test_env); +} + +/** + * env テスト。 + */ +static void test_env(void) +{ + // 事前設定 + KcEnv_remove("UT_TEST_ENV"); + + // 値設定 + bool ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + + // 上書き無し、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", false); + assert_true(ret); + char *env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("ABC", env_val); + + // 上書きあり、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", true); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("XYZ", env_val); + + // 削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // 存在しないキーを削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // キーに = が含まれる。 + ret = KcEnv_set("UT_TEST_ENV=XXX", "ABC", false); + assert_false(ret); + + // = が含まれたキーで取得 + ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV=XXX"); + assert_equals("ABC", env_val); + + // キーの値が 4096 超える + char key[8192] = {'\0'}; + for (int i = 0; i < 5000; i++) + { + key[i] = 'X'; + } + key[4999] = 'Z'; + ret = KcEnv_set(key, "XYZ", false); + assert_true(ret); + + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); + + // = 以降は無視されて値が取得されることを確認 + key[5000] = '='; + key[5001] = 'A'; + key[5002] = 'B'; + key[5003] = 'C'; + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); +} diff --git a/modules/test/src/test_map.c b/modules/test/src/test_map.c new file mode 100644 index 0000000..9953336 --- /dev/null +++ b/modules/test/src/test_map.c @@ -0,0 +1,298 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_map_new(void); +static void test_map_put(void); +static void test_map_remove(void); +static void test_map_entries(void); +static void test_map_malloc_error(void); + +/** + * KcMap 単体テストスイート + */ +void suite_map(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "map new/delete", test_map_new); + ut->add(ut, UT_TESTCASE, "map put/get", test_map_put); + ut->add(ut, UT_TESTCASE, "map remove", test_map_remove); + ut->add(ut, UT_TESTCASE, "map entries", test_map_entries); + ut->add(ut, UT_TESTCASE, "map malloc error", test_map_malloc_error); +} + +/** + * Map 生成/破棄。 + * + * @process KcMap_new を実行する。。 + * @result KcMap が生成されること。 + * + * @process KcMap_delete にて Map を破棄する。 + * @result Map が破棄されること。 + */ +static void test_map_new(void) +{ + KcMap *map = KcMap_new(0); + assert_not_null(map); + KcMap_delete(map); + + map = KcMap_new(65538); + assert_not_null(map); + KcMap_delete(map); + + // map にデータが残った状態での削除 + map = KcMap_new(10); + assert_not_null(map); + map->put(map, "key1", "value1", 7); + KcMap_delete(map); +} + +/** + * Map への追加/取得。 + */ +static void test_map_put(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // 値取得 + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + size_t size; + const char *value = (const char *)map->get(map, keys[i], &size); + assert_equals(vals[i], value); + } + + // 存在しないキーの値取得 + void *ptr = map->get(map, "ABC", NULL); + assert_null(ptr); + + // キー上書き登録 + res = (char *)map->put(map, "key10", "abcdefg", strlen("abcdefg") + 1); + assert_equals("abcdefg", (const char *)res); + res = (char *)map->get(map, "key10", NULL); + assert_equals("abcdefg", (const char *)res); + + KcMap_delete(map); +} + +/** + * Map からの削除/サイズ確認。 + */ +static void test_map_remove(void) +{ + KcMap *map = KcMap_new(3); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + int size_counter = 14; + map->remove(map, "key15"); + size_t size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key15", NULL); + assert_null(res); + + map->remove(map, "key7"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key7", NULL); + assert_null(res); + + map->remove(map, "key13"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key13", NULL); + assert_null(res); + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + // 存在しないキー削除 + map->remove(map, "abc"); + size = map->size(map); + assert_equals(9, size); + + KcMap_delete(map); + + // パターン2 + map = KcMap_new(3); + assert_not_null(map); + + size_counter = 3; + for (int i = 0; i < size_counter; i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + size_counter--; + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + KcMap_delete(map); +} + +// エントリ +typedef struct UserData_ +{ + int value; +} UserData; +static bool test_map_entries_handler(const char *key, const void *val, size_t size, void *args) +{ + UserData *user_data = (UserData *)args; + user_data->value--; + printf("key=%-5s, value=%-5s, size=%zu\n", key, (const char *)val, size); + return (user_data->value >= 0); +} + +/** + * Map エントリ取得 + */ +static void test_map_entries(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // entries + UserData args = { + .value = 10000}; + map->entries(map, test_map_entries_handler, &args); + + // entries [途中中断] + UserData args2 = { + .value = 5}; + map->entries(map, test_map_entries_handler, &args2); + + KcMap_delete(map); +} + +/** + * メモリ確保失敗 + */ +static void test_map_malloc_error(void) +{ + // 生成時失敗 + ut_alloc_control(0) + { + KcMap *map = KcMap_new(5); + assert_null(map); + } + + // put 時失敗 + KcMap *map = KcMap_new(5); + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + ut_alloc_control(0) + { + res = (char *)map->put(map, "abc", "def", 4); + assert_null(res); + } + KcMap_delete(map); +} \ No newline at end of file diff --git a/modules/test/src/ut.c b/modules/test/src/ut.c index c84b7b9..b921513 100644 --- a/modules/test/src/ut.c +++ b/modules/test/src/ut.c @@ -9,9 +9,12 @@ { // UT Setup suite_assert(); + suite_dl(); + suite_env(); suite_list_array(); suite_list_linked(); suite_lock_guard(); + suite_map(); suite_memory_dump(); suite_memory_entry(); suite_memory_listener(); @@ -27,7 +30,10 @@ return 0; } -// メモリテスト用 +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、メモリ管理操作用 +// static int ut_can_alloc_counter = 0; static bool ut_can_alloc( KcMemoryEntry *entry, size_t alignment, size_t size, diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/modules/test/a-test.gcno b/modules/test/a-test.gcno new file mode 100644 index 0000000..8152c12 --- /dev/null +++ b/modules/test/a-test.gcno Binary files differ diff --git a/modules/test/a.out b/modules/test/a.out new file mode 100755 index 0000000..933cb99 --- /dev/null +++ b/modules/test/a.out Binary files differ diff --git a/modules/test/src/test_dl.c b/modules/test/src/test_dl.c new file mode 100644 index 0000000..de5c0a0 --- /dev/null +++ b/modules/test/src/test_dl.c @@ -0,0 +1,63 @@ + +#include + +#include +#include +#include +#include + +#include "ut.h" + +#if (KC_IS_WINDOWS) +#define FILENAME "test-lib/libtest.dll" +#else +#define FILENAME "test-lib/libtest.so" +#endif + +// プロトタイプ宣言 +static void test_dl(void); + +/** + * KcDl 単体テストスイート + */ +void suite_dl(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "dl", test_dl); +} + +/** + * dl テスト。 + * + * @process 動的ライブラリをロードする。 + * @result ライブラリがロードされること。 + * + * @process ロードしたライブラリの関数を実行する。 + * @result 関数が実行されること。 + */ +static void test_dl(void) +{ + dl_handle_t handle = KcDl_open(FILENAME); + + // add + int (*test_add)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_add"); + int res = test_add(10, 20); + assert_equals(30, res); + + // sub + int (*test_sub)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_sub"); + res = test_sub(10, 20); + assert_equals(-10, res); + + // mul + int (*test_mul)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_mul"); + res = test_mul(10, 20); + assert_equals(200, res); + + // mul + int (*test_div)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_div"); + res = test_div(50, 10); + assert_equals(5, res); + + KcDl_close(handle); +} diff --git a/modules/test/src/test_env.c b/modules/test/src/test_env.c new file mode 100644 index 0000000..0321563 --- /dev/null +++ b/modules/test/src/test_env.c @@ -0,0 +1,84 @@ + +#include + +#include +#include +#include +#include + +static void test_env(void); + +/** + * env 単体テストスイート + */ +void suite_env(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "env", test_env); +} + +/** + * env テスト。 + */ +static void test_env(void) +{ + // 事前設定 + KcEnv_remove("UT_TEST_ENV"); + + // 値設定 + bool ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + + // 上書き無し、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", false); + assert_true(ret); + char *env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("ABC", env_val); + + // 上書きあり、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", true); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("XYZ", env_val); + + // 削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // 存在しないキーを削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // キーに = が含まれる。 + ret = KcEnv_set("UT_TEST_ENV=XXX", "ABC", false); + assert_false(ret); + + // = が含まれたキーで取得 + ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV=XXX"); + assert_equals("ABC", env_val); + + // キーの値が 4096 超える + char key[8192] = {'\0'}; + for (int i = 0; i < 5000; i++) + { + key[i] = 'X'; + } + key[4999] = 'Z'; + ret = KcEnv_set(key, "XYZ", false); + assert_true(ret); + + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); + + // = 以降は無視されて値が取得されることを確認 + key[5000] = '='; + key[5001] = 'A'; + key[5002] = 'B'; + key[5003] = 'C'; + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); +} diff --git a/modules/test/src/test_map.c b/modules/test/src/test_map.c new file mode 100644 index 0000000..9953336 --- /dev/null +++ b/modules/test/src/test_map.c @@ -0,0 +1,298 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_map_new(void); +static void test_map_put(void); +static void test_map_remove(void); +static void test_map_entries(void); +static void test_map_malloc_error(void); + +/** + * KcMap 単体テストスイート + */ +void suite_map(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "map new/delete", test_map_new); + ut->add(ut, UT_TESTCASE, "map put/get", test_map_put); + ut->add(ut, UT_TESTCASE, "map remove", test_map_remove); + ut->add(ut, UT_TESTCASE, "map entries", test_map_entries); + ut->add(ut, UT_TESTCASE, "map malloc error", test_map_malloc_error); +} + +/** + * Map 生成/破棄。 + * + * @process KcMap_new を実行する。。 + * @result KcMap が生成されること。 + * + * @process KcMap_delete にて Map を破棄する。 + * @result Map が破棄されること。 + */ +static void test_map_new(void) +{ + KcMap *map = KcMap_new(0); + assert_not_null(map); + KcMap_delete(map); + + map = KcMap_new(65538); + assert_not_null(map); + KcMap_delete(map); + + // map にデータが残った状態での削除 + map = KcMap_new(10); + assert_not_null(map); + map->put(map, "key1", "value1", 7); + KcMap_delete(map); +} + +/** + * Map への追加/取得。 + */ +static void test_map_put(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // 値取得 + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + size_t size; + const char *value = (const char *)map->get(map, keys[i], &size); + assert_equals(vals[i], value); + } + + // 存在しないキーの値取得 + void *ptr = map->get(map, "ABC", NULL); + assert_null(ptr); + + // キー上書き登録 + res = (char *)map->put(map, "key10", "abcdefg", strlen("abcdefg") + 1); + assert_equals("abcdefg", (const char *)res); + res = (char *)map->get(map, "key10", NULL); + assert_equals("abcdefg", (const char *)res); + + KcMap_delete(map); +} + +/** + * Map からの削除/サイズ確認。 + */ +static void test_map_remove(void) +{ + KcMap *map = KcMap_new(3); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + int size_counter = 14; + map->remove(map, "key15"); + size_t size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key15", NULL); + assert_null(res); + + map->remove(map, "key7"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key7", NULL); + assert_null(res); + + map->remove(map, "key13"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key13", NULL); + assert_null(res); + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + // 存在しないキー削除 + map->remove(map, "abc"); + size = map->size(map); + assert_equals(9, size); + + KcMap_delete(map); + + // パターン2 + map = KcMap_new(3); + assert_not_null(map); + + size_counter = 3; + for (int i = 0; i < size_counter; i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + size_counter--; + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + KcMap_delete(map); +} + +// エントリ +typedef struct UserData_ +{ + int value; +} UserData; +static bool test_map_entries_handler(const char *key, const void *val, size_t size, void *args) +{ + UserData *user_data = (UserData *)args; + user_data->value--; + printf("key=%-5s, value=%-5s, size=%zu\n", key, (const char *)val, size); + return (user_data->value >= 0); +} + +/** + * Map エントリ取得 + */ +static void test_map_entries(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // entries + UserData args = { + .value = 10000}; + map->entries(map, test_map_entries_handler, &args); + + // entries [途中中断] + UserData args2 = { + .value = 5}; + map->entries(map, test_map_entries_handler, &args2); + + KcMap_delete(map); +} + +/** + * メモリ確保失敗 + */ +static void test_map_malloc_error(void) +{ + // 生成時失敗 + ut_alloc_control(0) + { + KcMap *map = KcMap_new(5); + assert_null(map); + } + + // put 時失敗 + KcMap *map = KcMap_new(5); + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + ut_alloc_control(0) + { + res = (char *)map->put(map, "abc", "def", 4); + assert_null(res); + } + KcMap_delete(map); +} \ No newline at end of file diff --git a/modules/test/src/ut.c b/modules/test/src/ut.c index c84b7b9..b921513 100644 --- a/modules/test/src/ut.c +++ b/modules/test/src/ut.c @@ -9,9 +9,12 @@ { // UT Setup suite_assert(); + suite_dl(); + suite_env(); suite_list_array(); suite_list_linked(); suite_lock_guard(); + suite_map(); suite_memory_dump(); suite_memory_entry(); suite_memory_listener(); @@ -27,7 +30,10 @@ return 0; } -// メモリテスト用 +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、メモリ管理操作用 +// static int ut_can_alloc_counter = 0; static bool ut_can_alloc( KcMemoryEntry *entry, size_t alignment, size_t size, diff --git a/modules/test/src/ut.h b/modules/test/src/ut.h index 224e63b..de5fe82 100644 --- a/modules/test/src/ut.h +++ b/modules/test/src/ut.h @@ -5,9 +5,12 @@ #include extern void suite_assert(void); +extern void suite_dl(void); +extern void suite_env(void); extern void suite_list_array(void); extern void suite_list_linked(void); extern void suite_lock_guard(void); +extern void suite_map(void); extern void suite_memory_dump(void); extern void suite_memory_entry(void); extern void suite_memory_listener(void); @@ -15,6 +18,11 @@ extern void suite_memory(void); extern void suite_queue(void); +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、メモリ管理操作用 +// + extern bool (*_UT_KcMemory_can_alloc)( KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/modules/test/a-test.gcno b/modules/test/a-test.gcno new file mode 100644 index 0000000..8152c12 --- /dev/null +++ b/modules/test/a-test.gcno Binary files differ diff --git a/modules/test/a.out b/modules/test/a.out new file mode 100755 index 0000000..933cb99 --- /dev/null +++ b/modules/test/a.out Binary files differ diff --git a/modules/test/src/test_dl.c b/modules/test/src/test_dl.c new file mode 100644 index 0000000..de5c0a0 --- /dev/null +++ b/modules/test/src/test_dl.c @@ -0,0 +1,63 @@ + +#include + +#include +#include +#include +#include + +#include "ut.h" + +#if (KC_IS_WINDOWS) +#define FILENAME "test-lib/libtest.dll" +#else +#define FILENAME "test-lib/libtest.so" +#endif + +// プロトタイプ宣言 +static void test_dl(void); + +/** + * KcDl 単体テストスイート + */ +void suite_dl(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "dl", test_dl); +} + +/** + * dl テスト。 + * + * @process 動的ライブラリをロードする。 + * @result ライブラリがロードされること。 + * + * @process ロードしたライブラリの関数を実行する。 + * @result 関数が実行されること。 + */ +static void test_dl(void) +{ + dl_handle_t handle = KcDl_open(FILENAME); + + // add + int (*test_add)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_add"); + int res = test_add(10, 20); + assert_equals(30, res); + + // sub + int (*test_sub)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_sub"); + res = test_sub(10, 20); + assert_equals(-10, res); + + // mul + int (*test_mul)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_mul"); + res = test_mul(10, 20); + assert_equals(200, res); + + // mul + int (*test_div)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_div"); + res = test_div(50, 10); + assert_equals(5, res); + + KcDl_close(handle); +} diff --git a/modules/test/src/test_env.c b/modules/test/src/test_env.c new file mode 100644 index 0000000..0321563 --- /dev/null +++ b/modules/test/src/test_env.c @@ -0,0 +1,84 @@ + +#include + +#include +#include +#include +#include + +static void test_env(void); + +/** + * env 単体テストスイート + */ +void suite_env(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "env", test_env); +} + +/** + * env テスト。 + */ +static void test_env(void) +{ + // 事前設定 + KcEnv_remove("UT_TEST_ENV"); + + // 値設定 + bool ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + + // 上書き無し、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", false); + assert_true(ret); + char *env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("ABC", env_val); + + // 上書きあり、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", true); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("XYZ", env_val); + + // 削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // 存在しないキーを削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // キーに = が含まれる。 + ret = KcEnv_set("UT_TEST_ENV=XXX", "ABC", false); + assert_false(ret); + + // = が含まれたキーで取得 + ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV=XXX"); + assert_equals("ABC", env_val); + + // キーの値が 4096 超える + char key[8192] = {'\0'}; + for (int i = 0; i < 5000; i++) + { + key[i] = 'X'; + } + key[4999] = 'Z'; + ret = KcEnv_set(key, "XYZ", false); + assert_true(ret); + + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); + + // = 以降は無視されて値が取得されることを確認 + key[5000] = '='; + key[5001] = 'A'; + key[5002] = 'B'; + key[5003] = 'C'; + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); +} diff --git a/modules/test/src/test_map.c b/modules/test/src/test_map.c new file mode 100644 index 0000000..9953336 --- /dev/null +++ b/modules/test/src/test_map.c @@ -0,0 +1,298 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_map_new(void); +static void test_map_put(void); +static void test_map_remove(void); +static void test_map_entries(void); +static void test_map_malloc_error(void); + +/** + * KcMap 単体テストスイート + */ +void suite_map(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "map new/delete", test_map_new); + ut->add(ut, UT_TESTCASE, "map put/get", test_map_put); + ut->add(ut, UT_TESTCASE, "map remove", test_map_remove); + ut->add(ut, UT_TESTCASE, "map entries", test_map_entries); + ut->add(ut, UT_TESTCASE, "map malloc error", test_map_malloc_error); +} + +/** + * Map 生成/破棄。 + * + * @process KcMap_new を実行する。。 + * @result KcMap が生成されること。 + * + * @process KcMap_delete にて Map を破棄する。 + * @result Map が破棄されること。 + */ +static void test_map_new(void) +{ + KcMap *map = KcMap_new(0); + assert_not_null(map); + KcMap_delete(map); + + map = KcMap_new(65538); + assert_not_null(map); + KcMap_delete(map); + + // map にデータが残った状態での削除 + map = KcMap_new(10); + assert_not_null(map); + map->put(map, "key1", "value1", 7); + KcMap_delete(map); +} + +/** + * Map への追加/取得。 + */ +static void test_map_put(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // 値取得 + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + size_t size; + const char *value = (const char *)map->get(map, keys[i], &size); + assert_equals(vals[i], value); + } + + // 存在しないキーの値取得 + void *ptr = map->get(map, "ABC", NULL); + assert_null(ptr); + + // キー上書き登録 + res = (char *)map->put(map, "key10", "abcdefg", strlen("abcdefg") + 1); + assert_equals("abcdefg", (const char *)res); + res = (char *)map->get(map, "key10", NULL); + assert_equals("abcdefg", (const char *)res); + + KcMap_delete(map); +} + +/** + * Map からの削除/サイズ確認。 + */ +static void test_map_remove(void) +{ + KcMap *map = KcMap_new(3); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + int size_counter = 14; + map->remove(map, "key15"); + size_t size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key15", NULL); + assert_null(res); + + map->remove(map, "key7"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key7", NULL); + assert_null(res); + + map->remove(map, "key13"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key13", NULL); + assert_null(res); + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + // 存在しないキー削除 + map->remove(map, "abc"); + size = map->size(map); + assert_equals(9, size); + + KcMap_delete(map); + + // パターン2 + map = KcMap_new(3); + assert_not_null(map); + + size_counter = 3; + for (int i = 0; i < size_counter; i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + size_counter--; + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + KcMap_delete(map); +} + +// エントリ +typedef struct UserData_ +{ + int value; +} UserData; +static bool test_map_entries_handler(const char *key, const void *val, size_t size, void *args) +{ + UserData *user_data = (UserData *)args; + user_data->value--; + printf("key=%-5s, value=%-5s, size=%zu\n", key, (const char *)val, size); + return (user_data->value >= 0); +} + +/** + * Map エントリ取得 + */ +static void test_map_entries(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // entries + UserData args = { + .value = 10000}; + map->entries(map, test_map_entries_handler, &args); + + // entries [途中中断] + UserData args2 = { + .value = 5}; + map->entries(map, test_map_entries_handler, &args2); + + KcMap_delete(map); +} + +/** + * メモリ確保失敗 + */ +static void test_map_malloc_error(void) +{ + // 生成時失敗 + ut_alloc_control(0) + { + KcMap *map = KcMap_new(5); + assert_null(map); + } + + // put 時失敗 + KcMap *map = KcMap_new(5); + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + ut_alloc_control(0) + { + res = (char *)map->put(map, "abc", "def", 4); + assert_null(res); + } + KcMap_delete(map); +} \ No newline at end of file diff --git a/modules/test/src/ut.c b/modules/test/src/ut.c index c84b7b9..b921513 100644 --- a/modules/test/src/ut.c +++ b/modules/test/src/ut.c @@ -9,9 +9,12 @@ { // UT Setup suite_assert(); + suite_dl(); + suite_env(); suite_list_array(); suite_list_linked(); suite_lock_guard(); + suite_map(); suite_memory_dump(); suite_memory_entry(); suite_memory_listener(); @@ -27,7 +30,10 @@ return 0; } -// メモリテスト用 +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、メモリ管理操作用 +// static int ut_can_alloc_counter = 0; static bool ut_can_alloc( KcMemoryEntry *entry, size_t alignment, size_t size, diff --git a/modules/test/src/ut.h b/modules/test/src/ut.h index 224e63b..de5fe82 100644 --- a/modules/test/src/ut.h +++ b/modules/test/src/ut.h @@ -5,9 +5,12 @@ #include extern void suite_assert(void); +extern void suite_dl(void); +extern void suite_env(void); extern void suite_list_array(void); extern void suite_list_linked(void); extern void suite_lock_guard(void); +extern void suite_map(void); extern void suite_memory_dump(void); extern void suite_memory_entry(void); extern void suite_memory_listener(void); @@ -15,6 +18,11 @@ extern void suite_memory(void); extern void suite_queue(void); +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、メモリ管理操作用 +// + extern bool (*_UT_KcMemory_can_alloc)( KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); diff --git a/modules/test/test-lib/Makefile b/modules/test/test-lib/Makefile new file mode 100644 index 0000000..df5830c --- /dev/null +++ b/modules/test/test-lib/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libtest + +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +TARGET = $(NAME).dll +else +TARGET = $(NAME).so +endif + +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/modules/test/a-test.gcno b/modules/test/a-test.gcno new file mode 100644 index 0000000..8152c12 --- /dev/null +++ b/modules/test/a-test.gcno Binary files differ diff --git a/modules/test/a.out b/modules/test/a.out new file mode 100755 index 0000000..933cb99 --- /dev/null +++ b/modules/test/a.out Binary files differ diff --git a/modules/test/src/test_dl.c b/modules/test/src/test_dl.c new file mode 100644 index 0000000..de5c0a0 --- /dev/null +++ b/modules/test/src/test_dl.c @@ -0,0 +1,63 @@ + +#include + +#include +#include +#include +#include + +#include "ut.h" + +#if (KC_IS_WINDOWS) +#define FILENAME "test-lib/libtest.dll" +#else +#define FILENAME "test-lib/libtest.so" +#endif + +// プロトタイプ宣言 +static void test_dl(void); + +/** + * KcDl 単体テストスイート + */ +void suite_dl(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "dl", test_dl); +} + +/** + * dl テスト。 + * + * @process 動的ライブラリをロードする。 + * @result ライブラリがロードされること。 + * + * @process ロードしたライブラリの関数を実行する。 + * @result 関数が実行されること。 + */ +static void test_dl(void) +{ + dl_handle_t handle = KcDl_open(FILENAME); + + // add + int (*test_add)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_add"); + int res = test_add(10, 20); + assert_equals(30, res); + + // sub + int (*test_sub)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_sub"); + res = test_sub(10, 20); + assert_equals(-10, res); + + // mul + int (*test_mul)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_mul"); + res = test_mul(10, 20); + assert_equals(200, res); + + // mul + int (*test_div)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_div"); + res = test_div(50, 10); + assert_equals(5, res); + + KcDl_close(handle); +} diff --git a/modules/test/src/test_env.c b/modules/test/src/test_env.c new file mode 100644 index 0000000..0321563 --- /dev/null +++ b/modules/test/src/test_env.c @@ -0,0 +1,84 @@ + +#include + +#include +#include +#include +#include + +static void test_env(void); + +/** + * env 単体テストスイート + */ +void suite_env(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "env", test_env); +} + +/** + * env テスト。 + */ +static void test_env(void) +{ + // 事前設定 + KcEnv_remove("UT_TEST_ENV"); + + // 値設定 + bool ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + + // 上書き無し、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", false); + assert_true(ret); + char *env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("ABC", env_val); + + // 上書きあり、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", true); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("XYZ", env_val); + + // 削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // 存在しないキーを削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // キーに = が含まれる。 + ret = KcEnv_set("UT_TEST_ENV=XXX", "ABC", false); + assert_false(ret); + + // = が含まれたキーで取得 + ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV=XXX"); + assert_equals("ABC", env_val); + + // キーの値が 4096 超える + char key[8192] = {'\0'}; + for (int i = 0; i < 5000; i++) + { + key[i] = 'X'; + } + key[4999] = 'Z'; + ret = KcEnv_set(key, "XYZ", false); + assert_true(ret); + + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); + + // = 以降は無視されて値が取得されることを確認 + key[5000] = '='; + key[5001] = 'A'; + key[5002] = 'B'; + key[5003] = 'C'; + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); +} diff --git a/modules/test/src/test_map.c b/modules/test/src/test_map.c new file mode 100644 index 0000000..9953336 --- /dev/null +++ b/modules/test/src/test_map.c @@ -0,0 +1,298 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_map_new(void); +static void test_map_put(void); +static void test_map_remove(void); +static void test_map_entries(void); +static void test_map_malloc_error(void); + +/** + * KcMap 単体テストスイート + */ +void suite_map(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "map new/delete", test_map_new); + ut->add(ut, UT_TESTCASE, "map put/get", test_map_put); + ut->add(ut, UT_TESTCASE, "map remove", test_map_remove); + ut->add(ut, UT_TESTCASE, "map entries", test_map_entries); + ut->add(ut, UT_TESTCASE, "map malloc error", test_map_malloc_error); +} + +/** + * Map 生成/破棄。 + * + * @process KcMap_new を実行する。。 + * @result KcMap が生成されること。 + * + * @process KcMap_delete にて Map を破棄する。 + * @result Map が破棄されること。 + */ +static void test_map_new(void) +{ + KcMap *map = KcMap_new(0); + assert_not_null(map); + KcMap_delete(map); + + map = KcMap_new(65538); + assert_not_null(map); + KcMap_delete(map); + + // map にデータが残った状態での削除 + map = KcMap_new(10); + assert_not_null(map); + map->put(map, "key1", "value1", 7); + KcMap_delete(map); +} + +/** + * Map への追加/取得。 + */ +static void test_map_put(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // 値取得 + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + size_t size; + const char *value = (const char *)map->get(map, keys[i], &size); + assert_equals(vals[i], value); + } + + // 存在しないキーの値取得 + void *ptr = map->get(map, "ABC", NULL); + assert_null(ptr); + + // キー上書き登録 + res = (char *)map->put(map, "key10", "abcdefg", strlen("abcdefg") + 1); + assert_equals("abcdefg", (const char *)res); + res = (char *)map->get(map, "key10", NULL); + assert_equals("abcdefg", (const char *)res); + + KcMap_delete(map); +} + +/** + * Map からの削除/サイズ確認。 + */ +static void test_map_remove(void) +{ + KcMap *map = KcMap_new(3); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + int size_counter = 14; + map->remove(map, "key15"); + size_t size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key15", NULL); + assert_null(res); + + map->remove(map, "key7"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key7", NULL); + assert_null(res); + + map->remove(map, "key13"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key13", NULL); + assert_null(res); + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + // 存在しないキー削除 + map->remove(map, "abc"); + size = map->size(map); + assert_equals(9, size); + + KcMap_delete(map); + + // パターン2 + map = KcMap_new(3); + assert_not_null(map); + + size_counter = 3; + for (int i = 0; i < size_counter; i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + size_counter--; + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + KcMap_delete(map); +} + +// エントリ +typedef struct UserData_ +{ + int value; +} UserData; +static bool test_map_entries_handler(const char *key, const void *val, size_t size, void *args) +{ + UserData *user_data = (UserData *)args; + user_data->value--; + printf("key=%-5s, value=%-5s, size=%zu\n", key, (const char *)val, size); + return (user_data->value >= 0); +} + +/** + * Map エントリ取得 + */ +static void test_map_entries(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // entries + UserData args = { + .value = 10000}; + map->entries(map, test_map_entries_handler, &args); + + // entries [途中中断] + UserData args2 = { + .value = 5}; + map->entries(map, test_map_entries_handler, &args2); + + KcMap_delete(map); +} + +/** + * メモリ確保失敗 + */ +static void test_map_malloc_error(void) +{ + // 生成時失敗 + ut_alloc_control(0) + { + KcMap *map = KcMap_new(5); + assert_null(map); + } + + // put 時失敗 + KcMap *map = KcMap_new(5); + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + ut_alloc_control(0) + { + res = (char *)map->put(map, "abc", "def", 4); + assert_null(res); + } + KcMap_delete(map); +} \ No newline at end of file diff --git a/modules/test/src/ut.c b/modules/test/src/ut.c index c84b7b9..b921513 100644 --- a/modules/test/src/ut.c +++ b/modules/test/src/ut.c @@ -9,9 +9,12 @@ { // UT Setup suite_assert(); + suite_dl(); + suite_env(); suite_list_array(); suite_list_linked(); suite_lock_guard(); + suite_map(); suite_memory_dump(); suite_memory_entry(); suite_memory_listener(); @@ -27,7 +30,10 @@ return 0; } -// メモリテスト用 +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、メモリ管理操作用 +// static int ut_can_alloc_counter = 0; static bool ut_can_alloc( KcMemoryEntry *entry, size_t alignment, size_t size, diff --git a/modules/test/src/ut.h b/modules/test/src/ut.h index 224e63b..de5fe82 100644 --- a/modules/test/src/ut.h +++ b/modules/test/src/ut.h @@ -5,9 +5,12 @@ #include extern void suite_assert(void); +extern void suite_dl(void); +extern void suite_env(void); extern void suite_list_array(void); extern void suite_list_linked(void); extern void suite_lock_guard(void); +extern void suite_map(void); extern void suite_memory_dump(void); extern void suite_memory_entry(void); extern void suite_memory_listener(void); @@ -15,6 +18,11 @@ extern void suite_memory(void); extern void suite_queue(void); +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、メモリ管理操作用 +// + extern bool (*_UT_KcMemory_can_alloc)( KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); diff --git a/modules/test/test-lib/Makefile b/modules/test/test-lib/Makefile new file mode 100644 index 0000000..df5830c --- /dev/null +++ b/modules/test/test-lib/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libtest + +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +TARGET = $(NAME).dll +else +TARGET = $(NAME).so +endif + +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/test/test-lib/src/test.c b/modules/test/test-lib/src/test.c new file mode 100644 index 0000000..248eed9 --- /dev/null +++ b/modules/test/test-lib/src/test.c @@ -0,0 +1,21 @@ + +int test_add(int v1, int v2) +{ + return (v1 + v2); +} + +int test_sub(int v1, int v2) +{ + return (v1 - v2); +} + +int test_mul(int v1, int v2) +{ + return (v1 * v2); +} + +int test_div(int v1, int v2) +{ + return (v1 / v2); +} + diff --git a/config.mk b/config.mk index dd4c015..a0ac27c 100644 --- a/config.mk +++ b/config.mk @@ -16,14 +16,24 @@ # ARCH ?= x86_64 # ARCH ?= aarch64 +# ARCH ?= i686-w64-mingw32 # # クロスコンパイラ指定 # -ifeq ($(strip $(ARCH)),aarch64) -CROSS_COMPILE = +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +# for Windows +CROSS_COMPILE ?=i686-w64-mingw32- +LIBS ?= -lws2_32 else -CROSS_COMPILE = +# For Linux +ifeq ($(strip $(ARCH)),aarch64) +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +else +CROSS_COMPILE ?= +LIBS ?= -lpthread -lrt +endif endif # @@ -51,7 +61,7 @@ # # 共通でリンクするライブラリを指定します。 # -LIBS += -lpthread -lrt +LIBS += CFLAGS += -DKC_MEMORY_ENABLED=1 diff --git a/lib/libtest.dll b/lib/libtest.dll new file mode 100755 index 0000000..8d87e70 --- /dev/null +++ b/lib/libtest.dll Binary files differ diff --git a/mk/link-dll-conf.mk b/mk/link-dll-conf.mk new file mode 100644 index 0000000..c470c17 --- /dev/null +++ b/mk/link-dll-conf.mk @@ -0,0 +1,22 @@ +# ============================================================================== +# 動的ライブラリ 生成に関する設定 +# ============================================================================== +# +# -fPIC オプションを付与する。 +# +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) +CFLAGS += -fPIC +CXXFLAGS += -fPIC + +TOP_TARGET = $(addprefix $(TOPDIR)/lib/,$(TARGET)) + +HEADER_FILES = $(wildcard include/*.h) $(wildcard include/*.hpp) +TOP_HEADER_FILES = $(addprefix $(TOPDIR)/include/,$(notdir $(HEADER_FILES))) + +CLEAN_FILES += $(TOP_TARGET) +CLEAN_FILES += $(TOP_HEADER_FILES) + +endif +endif + diff --git a/mk/link-dll-rule.mk b/mk/link-dll-rule.mk new file mode 100644 index 0000000..0084d31 --- /dev/null +++ b/mk/link-dll-rule.mk @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 動的ライブラリ 生成ルール +# ------------------------------------------------------------------------------ +ifneq ($(strip $(NAME)),) +ifeq ($(strip $(NAME).dll),$(strip $(TARGET))) + +## ----------------------------------------------------------------------------- +## TOPディレクトリへのコピー +## ----------------------------------------------------------------------------- +$(TOP_TARGET): $(TARGET) +ifneq ($(strip $(HEADER_FILES)),) + $(CP) -f $(HEADER_FILES) $(TOPDIR)/include/ +endif + $(CP) -f -d $(TARGET)* $(TOPDIR)/lib/ + + +## ----------------------------------------------------------------------------- +## バージョン番号無し so ファイル生成 +## ----------------------------------------------------------------------------- +# .dllファイル生成 +$(TARGET): $(OBJS) + $(LINK) $(LDFLAGS) -shared -Wl,--out-implib,$(TARGET:.dll=).lib -o $(TARGET) $^ $(LIBS) + + +endif +endif + diff --git a/modules/include/kc_assert.h b/modules/include/kc_assert.h index 03fc71d..7d1c18f 100644 --- a/modules/include/kc_assert.h +++ b/modules/include/kc_assert.h @@ -38,7 +38,8 @@ _Generic((expected), \ int: assert_equals_long_, \ long: assert_equals_long_, \ - char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) + char *: assert_equals_string_, \ + const char *: assert_equals_string_)(expected, actual, __FILE__, __func__, __LINE__) #define assert_equals_3(expected, actual, delta) \ _Generic((expected), \ diff --git a/modules/include/kc_dl.h b/modules/include/kc_dl.h new file mode 100644 index 0000000..4f303e2 --- /dev/null +++ b/modules/include/kc_dl.h @@ -0,0 +1,55 @@ +/** + * @file kc_dl.h + * @brief 動的ライブラリモジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_DL_H +#define KC_DL_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + +#if (KC_IS_WINDOWS) + typedef HINSTANCE dl_handle_t; +#else +#include +typedef void *dl_handle_t; +#endif + + /** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ + dl_handle_t KcDl_open(const char *filename); + + /** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ + void *KcDl_sym(dl_handle_t handle, const char *symbol); + + /** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ + bool KcDl_close(dl_handle_t handle); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_DL_H diff --git a/modules/include/kc_env.h b/modules/include/kc_env.h new file mode 100644 index 0000000..fb3ca35 --- /dev/null +++ b/modules/include/kc_env.h @@ -0,0 +1,57 @@ +/** + * @file kc_env.h + * @brief 環境変数モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_ENV_H +#define KC_ENV_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + char *KcEnv_get(const char *name); + + /** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool KcEnv_set(const char *name, const char *value, bool overwrite); + + /** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool KcEnv_remove(const char *name); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_ENV_H diff --git a/modules/include/kc_list.h b/modules/include/kc_list.h index 5e279fb..8bf6c7b 100644 --- a/modules/include/kc_list.h +++ b/modules/include/kc_list.h @@ -21,7 +21,7 @@ #endif /** - * 単一種類の要素を扱うことが可能なリスト。 + * リスト。 */ typedef struct KcList_ { diff --git a/modules/include/kc_map.h b/modules/include/kc_map.h new file mode 100644 index 0000000..ba78845 --- /dev/null +++ b/modules/include/kc_map.h @@ -0,0 +1,114 @@ +/** + * @file kc_map.h + * @brief MAp モジュールヘッダファイル + * @copyright 2002 - 2023 Nomura Kei + * @depends + * kc.h + */ +#ifndef KC_LIST_H +#define KC_LIST_H + +#include + +#ifdef __cplusplus +extern "C" +{ + namespace kc + { + using namespace std; +#endif + + /** + * マップ。 + */ + typedef struct KcMap_ + { + /** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ + size_t (*size)(struct KcMap_ *map); + + /** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ + void *(*put)(struct KcMap_ *map, const char *key, const void *obj, size_t size); + + /** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ + void *(*get)(struct KcMap_ *map, const char *key, size_t *size); + + /** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ + void (*remove)(struct KcMap_ *map, const char *key); + + /** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ + void (*clear)(struct KcMap_ *map); + + /** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ + void (*entries)(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); + + /** + * マップ管理情報 + * 本オブジェクトはリスト実装者が利用するための情報へのポインタとなります。 + */ + void *_info; + + } KcMap; + + /** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ + KcMap *KcMap_new(size_t cap); + + /** + * マップを破棄します。 + * @param map 破棄するマップ + */ + void KcMap_delete(KcMap *map); + + /** + * 指定されたキーに対応するハッシュコードを返します。 + */ + int KcMap_hash_code(const char *key); + +#ifdef __cplusplus + } // namespace kc +} // extern "C" +#endif +#endif // KC_LIST_H diff --git a/modules/src/kc_dl.c b/modules/src/kc_dl.c new file mode 100644 index 0000000..8b7b56e --- /dev/null +++ b/modules/src/kc_dl.c @@ -0,0 +1,56 @@ +/** + * @file kc_dl.c + * @brief 動的ライブラリモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include + +/** + * 指定された動的ライブラリをオープンします。 + * + * @param filename 動的ライブラリのファイル名 + */ +dl_handle_t KcDl_open(const char *filename) +{ + dl_handle_t handle; +#if (KC_IS_WINDOWS) + handle = LoadLibrary(filename); +#else + handle = dlopen(filename, RTLD_NOW); +#endif + return handle; +} + +/** + * 動的ライブラリの関数を取得します。 + * + * @param handle ハンドル + * @param symbol 関数のシンボル名 + * @return 関数 + */ +void *KcDl_sym(dl_handle_t handle, const char *symbol) +{ + void *func; +#if (KC_IS_WINDOWS) + func = (void *)GetProcAddress(handle, symbol); +#else + func = dlsym(handle, symbol); +#endif + return func; +} + +/** + * 動的ライブラリをクローズします。 + * + * @param handle クローズするハンドル + */ +bool KcDl_close(dl_handle_t handle) +{ +#if (KC_IS_WINDOWS) + BOOL ret = FreeLibrary(handle); + return (bool)ret; +#else + int ret = dlclose(handle); + return (ret == 0); +#endif +} \ No newline at end of file diff --git a/modules/src/kc_env.c b/modules/src/kc_env.c new file mode 100644 index 0000000..0675c73 --- /dev/null +++ b/modules/src/kc_env.c @@ -0,0 +1,123 @@ +/** + * @file kc_env.c + * @brief 環境変数 モジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include + +#include +#include + +#define KC_ENV_BUFFER_MAX (4096) + +/** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ +char *KcEnv_get(const char *name) +{ + char tmp_buff[KC_ENV_BUFFER_MAX]; + const char *key_name = tmp_buff; + + char *tmp_ptr = strstr(name, "="); + if (tmp_ptr) + { // = が含まれる。 + size_t len = (size_t)(tmp_ptr - name); + if (len < KC_ENV_BUFFER_MAX) + { + tmp_ptr = NULL; + strncpy(tmp_buff, name, len); + } + else + { + tmp_ptr = (char *)malloc(len + 1); + strncpy(tmp_ptr, name, len + 1); + tmp_ptr[len] = '\0'; + key_name = tmp_ptr; + } + } + else + { + key_name = name; + } + + char *result = NULL; +#if (KC_IS_WINDOWS) + static char buff[KC_ENV_BUFFER_MAX]; + DWORD ret = GetEnvironmentVarable(key_name, (LPSTR)buff, sizeof(buff)); + if (ret != 0) + { + result = buff; + } +#else + result = getenv(key_name); +#endif + // malloc でメモリを確保している場合、解放する。 + // ※確保していない場合、tmp_ptr = NULL のため、free(NULL) となり副作用なし + free(tmp_ptr); + return result; +} + +/** + * 指定された name の環境変数が存在しない場合、 + * 環境変数 name に指定された値 value を設定します。 + * overwrite が true ならばその値を value に変更します。 + * false ならば name の値を変更せず、true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し、false を返します。 + * + * @param name 環境変数名 + * @param value 設定する環境変数の値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ +bool KcEnv_set(const char *name, const char *value, bool overwrite) +{ +#if (KC_IS_WINDOWS) + // = が含まれている場合はエラー + char *ptr = strstr(name, "="); + if (ptr) + { + LPSTR buff[1]; + DWORD ret = GetEnvironmentVariable(name, (LPSTR)buff, 1); + if ((!overwrite) && (ret != 0)) + { // 上書きなし && 既に環境変数に存在するので true を返す。 + return true; + } + + // 環境変数設定 + ret = SetEnvironmentVariable(name, value); + return (ret != 0); + } + else + { + errno = EINVAL; + return false; + } +#else + int ret = setenv(name, value, overwrite); + return (ret == 0); +#endif +} + +/** + * 変数 name を環境から削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ +bool KcEnv_remove(const char *name) +{ +#if (KC_IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif +} diff --git a/modules/src/kc_map.c b/modules/src/kc_map.c new file mode 100644 index 0000000..c2fbd3f --- /dev/null +++ b/modules/src/kc_map.c @@ -0,0 +1,417 @@ +/** + * @file kc_map.c + * @brief マップモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include + +#include +#include +#include + +#define KC_MAP_CAPACITY_MAX (65536) + +/** + * KcMapEntry 情報 + */ +typedef struct KcMapEntry_ +{ + char *key; //!< キー + size_t key_size; //!< キーのためのメモリ割り当てサイズ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcMapEntry_ *next; //!< 次のエントリへのポインタ + struct KcMapEntry_ *prev; //!< 一つ前のエントリへのポインタ +} KcMapEntry; + +/** + * KcMap 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + KcMapEntry **table; //!< ハッシュ用テーブル + size_t table_size; //!< ハッシュ用テーブルサイズ + size_t size; //!< 要素数 +} KcMapInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= + +static size_t KcMap_size(struct KcMap_ *map); +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size); +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size); +static void KcMap_remove(struct KcMap_ *map, const char *key); +static void KcMap_clear(struct KcMap_ *map); +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args); +static size_t KcMap_capacity(size_t cap); +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size); +static size_t KcMapEntry_key_size(const char *key); +static KcMapEntry *KcMapEntry_search(KcMapInfo *info, const char *key); + +/** + * マップ を構築します。 + * + * @return cap マップのキー管理容量 + */ +KcMap *KcMap_new(size_t cap) +{ + // KcMap の管理構造 + // +--------------+ + // | KcMap | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | **table -----------+ + // | table_size | | + // +--------------+ | + // |
| <---+ + // | | + // +--------------+ + size_t new_cap = KcMap_capacity(cap); + KcMap *map = (KcMap *)malloc( + sizeof(KcMap) + sizeof(KcMapInfo) + (sizeof(KcMapEntry) * new_cap)); + if (map != NULL) + { + map->size = KcMap_size; + map->put = KcMap_put; + map->get = KcMap_get; + map->remove = KcMap_remove; + map->clear = KcMap_clear; + map->entries = KcMap_entries; + map->_info = (map + 1); + + KcMapInfo *info = (KcMapInfo *)map->_info; + info->table = (KcMapEntry **)(info + 1); + info->table_size = new_cap; + for (int i = 0; i < (int)new_cap; i++) + { // Table 初期化 + info->table[i] = NULL; + } + + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->size = 0; + } + return map; +} + +/** + * マップを破棄します。 + * @param map 破棄するマップ + */ +void KcMap_delete(KcMap *map) +{ + map->clear(map); + free(map); +} + +/** + * 指定されたキーに対応するハッシュコードを返します。 + */ +int KcMap_hash_code(const char *key) +{ + int code = 0; + const char *ch = key; + while (*ch != '\0') + { + code = code * 31 + *ch; + ch++; + } + return code; +} + +/** + * マップに格納されている要素の数を返します。 + * + * @param map 対象マップ + * @return 要素の数 + */ +static size_t KcMap_size(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +/** + * マップに要素を追加します。 + * オブジェクトはコピーして追加されます。 + * 追加に失敗した場合、NULL が返されます。 + * + * @param map 対象マップ + * @param key キー + * @param obj キーに対応するオブジェクト + * @param size オブジェクトのサイズ + * @return 追加したオブジェクトへのポインタ + */ +static void *KcMap_put(struct KcMap_ *map, const char *key, const void *obj, size_t size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + int hash = KcMap_hash_code(key); + KcMapEntry *entry = KcMapEntry_new(key, obj, size); + if (entry != NULL) + { + kc_lock_guard(&(info->mutex)) + { // 既に同じキーが登録されている場合、一度削除する。 + map->remove(map, key); + + // table_size は 2のべき乗 + // => -1 した値は、その値を上限として全てのビットが立つ。 + // 例) 8 -> (8-1) = 0b00000111 + // AND を取ると hash は、0-7 のテーブルのインデックス範囲となる。 + hash = hash & (info->table_size - 1); + + KcMapEntry *tmp_entry = info->table[hash]; + if (tmp_entry == NULL) + { // テーブルに入っていないため、先頭に設定 + info->table[hash] = entry; + info->size++; + } + else + { + while (tmp_entry->next != NULL) + { // テーブルの先のリンクがなくなるまでたどる + tmp_entry = tmp_entry->next; + } + tmp_entry->next = entry; + entry->prev = tmp_entry; + info->size++; + } + } + return entry->value; + } + else + { + return NULL; + } +} + +/** + * 指定されたキーに対応するオブジェクトを取得します。 + * オブジェクトは参照が渡されます。 + * size が NULL でない場合、取得されたオブジェクトのサイズが格納されます。 + * + * @param map 対象マップ + * @param key キー + * @param size オブジェクトのサイズ + * @return オブジェクトへのポインタ + */ +static void *KcMap_get(struct KcMap_ *map, const char *key, size_t *size) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + void *res_obj = NULL; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *entry = KcMapEntry_search(info, key); + if (entry != NULL) + { + if (size) + { + *size = entry->size; + } + res_obj = entry->value; + } + } + return res_obj; +} + +/** + * 指定されたキーに対応するオブジェクトを削除します。 + * + * @param map 対象マップ + * @param key キー + */ +static void KcMap_remove(struct KcMap_ *map, const char *key) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + // 指定されたキーに対応するエントリを探す。 + KcMapEntry *remove_entry = KcMapEntry_search(info, key); + if (remove_entry != NULL) + { + if (remove_entry->prev) + { // 削除対象がテーブルの先のリストにある + remove_entry->prev->next = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = remove_entry->prev; + } + } + else + { // 削除対象が table の配列要素 + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + info->table[hash] = remove_entry->next; + if (remove_entry->next) + { + remove_entry->next->prev = NULL; + } + } + free(remove_entry); + info->size--; + } + } +} + +/** + * マップ内のすべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcMap_clear(struct KcMap_ *map) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + KcMapEntry *tmp_entry; + for (int i = 0; i < (int)info->table_size; i++) + { + for (KcMapEntry *entry = info->table[i]; entry != NULL; entry = tmp_entry) + { + tmp_entry = entry->next; + free(entry); + } + info->table[i] = NULL; + } + info->size = 0; + } +} + +/** + * マップに格納されている要素を引数に、指定されたハンドラを実行します。 + * ハンドラ関数が false を返す場合、呼出し処置は中断されます。 + * + * @param map 対象マップ + * @param handler ハンドラ関数 + * @param args ハンドラ関数に渡されるユーザーデータ + */ +static void KcMap_entries(struct KcMap_ *map, bool (*handler)(const char *key, const void *val, size_t size, void *args), void *args) +{ + KcMapInfo *info = (KcMapInfo *)map->_info; + kc_lock_guard(&(info->mutex)) + { + bool is_continue = true; + for (int i = 0; (i < (int)info->table_size) && is_continue; i++) + { + for (KcMapEntry *entry = info->table[i]; (entry != NULL && is_continue); entry = entry->next) + { + is_continue = handler(entry->key, entry->value, entry->size, args); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// [内部関数] +// + +/** + * 指定された容量を 2 のべき乗に揃えます。 + * cap に 0 または、 KC_MAP_CAPACITY_MAX より大きい値が指定された場合、 + * KC_MAP_CAPACITY_MAP の容量となります。 + * + * @param cap 容量 + * @return 調整された容量 + */ +static size_t KcMap_capacity(size_t cap) +{ + size_t new_cap = 1; + if ((cap < 1) || (KC_MAP_CAPACITY_MAX < cap)) + { + new_cap = KC_MAP_CAPACITY_MAX; + } + else + { + while (new_cap < cap) + { + new_cap <<= 1; + } + } + return new_cap; +} + +/** + * KcMapEntry を生成します。 + * 生成に失敗した場合、NULL を返します。 + * + * @param key キー + * @param obj 値 + * @param size 値のサイズ + * @return 生成された KeyMapEntry + */ +static KcMapEntry *KcMapEntry_new(const char *key, const void *obj, size_t size) +{ + // KcMapEntry の構成 + // +--------------+ + // | | + // | key -------------+ + // | value -----------|--+ + // | size | | | + // | next | | | + // +--------------+ | | + // | |<--+ | + // | |<-----+ + // +--------------+ + size_t key_size = KcMapEntry_key_size(key); + size_t new_size = sizeof(KcMapEntry) + key_size + size; + KcMapEntry *entry = (KcMapEntry *)malloc(new_size); + if (entry != NULL) + { + entry->key = (char *)(entry + 1); + entry->key_size = key_size; + entry->value = &entry->key[entry->key_size]; + entry->size = size; + entry->next = NULL; + entry->prev = NULL; + strncpy(entry->key, key, entry->key_size); + memcpy(entry->value, obj, size); + } + return entry; +} + +/** + * キーのメモリ割り当てサイズを取得します。 + * キーのメモリ割り当ては、ポインタの整数倍に揃えられます。 + * + * @param key キー + * @return キーのメモリ割り当てサイズ + */ +static size_t KcMapEntry_key_size(const char *key) +{ + size_t size = strlen(key) + 1; + int nmemb = size / sizeof(void *); + return (sizeof(void *) * (nmemb + 1)); +} + +/** + * 指定されたキーに対応するエントリを取得します。 + * + * @param info マップ情報 + * @param key キー + * @return キーに対応するエントリ + */ +static KcMapEntry *KcMapEntry_search( + KcMapInfo *info, const char *key) +{ + int hash = KcMap_hash_code(key); + hash = hash & (info->table_size - 1); + KcMapEntry *entry = (info->table)[hash]; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { // キーが一致 + break; + } + entry = entry->next; + } + return entry; +} diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index 5bb141d..6bca53c 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -10,7 +10,6 @@ // 最大登録関数 #define KC_UT_ENTRY_MAX (16384) -#define KC_UT_OUTPUT stderr /** * テスト用関数リスト。 @@ -43,13 +42,15 @@ static void KcUt_run(KcUt *ut); // 公開関数 +FILE *kc_ut_output = NULL; /** * 単体テスト用のインスタンスを取得します。 * * @return 単体テスト用の唯一のインスタンス */ -KcUt *KcUt_get_instance(void) +KcUt * +KcUt_get_instance(void) { static KcUt instance; static KcUtInfo info = { @@ -66,6 +67,7 @@ instance.add = KcUt_add; instance.run = KcUt_run; instance._info = &info; + kc_ut_output = stderr; } return &instance; } @@ -81,7 +83,8 @@ { KcUt *ut = KcUt_get_instance(); ((KcUtInfo *)ut->_info)->is_ng = true; - fprintf(stderr, KC_TERM_H_YEL "%s\n" KC_TERM_DEF KC_TERM_CLR, msg); + fprintf(kc_ut_output, KC_TERM_H_YEL "%s\n", msg); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } @@ -136,18 +139,21 @@ result = KC_TERM_RED "NG" KC_TERM_DEF; } // 実行結果表示 - printf( - KC_TERM_BLD KC_TERM_CYN - "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n" KC_TERM_DEF KC_TERM_CLR, - info->test_counter, - info->entry[info->run_index].title, - result); + fprintf(kc_ut_output, + KC_TERM_BLD KC_TERM_CYN + "[No.%05d] %-65s [ %s" KC_TERM_CYN " ]\n", + info->test_counter, + info->entry[info->run_index].title, + result); fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } } - printf(KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); - printf(KC_TERM_GRN " Success : %-5d\n", info->ok_counter); - printf(KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); - printf(KC_TERM_CYN " Total : %-5d\n" KC_TERM_DEF KC_TERM_CLR, info->test_counter); - printf("\n"); + fprintf(kc_ut_output, KC_TERM_BLD KC_TERM_CYN "===== Result ==========================================================\n"); + fprintf(kc_ut_output, KC_TERM_GRN " Success : %-5d\n", info->ok_counter); + fprintf(kc_ut_output, KC_TERM_RED " Failuer : %-5d\n", info->ng_counter); + fprintf(kc_ut_output, KC_TERM_CYN " Total : %-5d\n", info->test_counter); + fprintf(kc_ut_output, "\n"); + fprintf(stderr, KC_TERM_DEF KC_TERM_CLR); + fprintf(stdout, KC_TERM_DEF KC_TERM_CLR); } diff --git a/modules/test/Makefile b/modules/test/Makefile index 41c513f..29cc50b 100644 --- a/modules/test/Makefile +++ b/modules/test/Makefile @@ -1,4 +1,4 @@ -g ============================================================================== +# ============================================================================== # Makefile # ============================================================================== # diff --git a/modules/test/a-test.d b/modules/test/a-test.d new file mode 100644 index 0000000..597902c --- /dev/null +++ b/modules/test/a-test.d @@ -0,0 +1 @@ +test.o: test.c diff --git a/modules/test/a-test.gcno b/modules/test/a-test.gcno new file mode 100644 index 0000000..8152c12 --- /dev/null +++ b/modules/test/a-test.gcno Binary files differ diff --git a/modules/test/a.out b/modules/test/a.out new file mode 100755 index 0000000..933cb99 --- /dev/null +++ b/modules/test/a.out Binary files differ diff --git a/modules/test/src/test_dl.c b/modules/test/src/test_dl.c new file mode 100644 index 0000000..de5c0a0 --- /dev/null +++ b/modules/test/src/test_dl.c @@ -0,0 +1,63 @@ + +#include + +#include +#include +#include +#include + +#include "ut.h" + +#if (KC_IS_WINDOWS) +#define FILENAME "test-lib/libtest.dll" +#else +#define FILENAME "test-lib/libtest.so" +#endif + +// プロトタイプ宣言 +static void test_dl(void); + +/** + * KcDl 単体テストスイート + */ +void suite_dl(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "dl", test_dl); +} + +/** + * dl テスト。 + * + * @process 動的ライブラリをロードする。 + * @result ライブラリがロードされること。 + * + * @process ロードしたライブラリの関数を実行する。 + * @result 関数が実行されること。 + */ +static void test_dl(void) +{ + dl_handle_t handle = KcDl_open(FILENAME); + + // add + int (*test_add)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_add"); + int res = test_add(10, 20); + assert_equals(30, res); + + // sub + int (*test_sub)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_sub"); + res = test_sub(10, 20); + assert_equals(-10, res); + + // mul + int (*test_mul)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_mul"); + res = test_mul(10, 20); + assert_equals(200, res); + + // mul + int (*test_div)(int, int) = (int (*)(int, int))KcDl_sym(handle, "test_div"); + res = test_div(50, 10); + assert_equals(5, res); + + KcDl_close(handle); +} diff --git a/modules/test/src/test_env.c b/modules/test/src/test_env.c new file mode 100644 index 0000000..0321563 --- /dev/null +++ b/modules/test/src/test_env.c @@ -0,0 +1,84 @@ + +#include + +#include +#include +#include +#include + +static void test_env(void); + +/** + * env 単体テストスイート + */ +void suite_env(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "env", test_env); +} + +/** + * env テスト。 + */ +static void test_env(void) +{ + // 事前設定 + KcEnv_remove("UT_TEST_ENV"); + + // 値設定 + bool ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + + // 上書き無し、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", false); + assert_true(ret); + char *env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("ABC", env_val); + + // 上書きあり、既に存在する。 + ret = KcEnv_set("UT_TEST_ENV", "XYZ", true); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV"); + assert_equals("XYZ", env_val); + + // 削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // 存在しないキーを削除 + ret = KcEnv_remove("UT_TEST_ENV"); + assert_true(ret); + + // キーに = が含まれる。 + ret = KcEnv_set("UT_TEST_ENV=XXX", "ABC", false); + assert_false(ret); + + // = が含まれたキーで取得 + ret = KcEnv_set("UT_TEST_ENV", "ABC", false); + assert_true(ret); + env_val = KcEnv_get("UT_TEST_ENV=XXX"); + assert_equals("ABC", env_val); + + // キーの値が 4096 超える + char key[8192] = {'\0'}; + for (int i = 0; i < 5000; i++) + { + key[i] = 'X'; + } + key[4999] = 'Z'; + ret = KcEnv_set(key, "XYZ", false); + assert_true(ret); + + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); + + // = 以降は無視されて値が取得されることを確認 + key[5000] = '='; + key[5001] = 'A'; + key[5002] = 'B'; + key[5003] = 'C'; + env_val = KcEnv_get(key); + assert_not_null(env_val); + assert_equals("XYZ", env_val); +} diff --git a/modules/test/src/test_map.c b/modules/test/src/test_map.c new file mode 100644 index 0000000..9953336 --- /dev/null +++ b/modules/test/src/test_map.c @@ -0,0 +1,298 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ut.h" + +// プロトタイプ宣言 +static void test_map_new(void); +static void test_map_put(void); +static void test_map_remove(void); +static void test_map_entries(void); +static void test_map_malloc_error(void); + +/** + * KcMap 単体テストスイート + */ +void suite_map(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_TESTCASE, "map new/delete", test_map_new); + ut->add(ut, UT_TESTCASE, "map put/get", test_map_put); + ut->add(ut, UT_TESTCASE, "map remove", test_map_remove); + ut->add(ut, UT_TESTCASE, "map entries", test_map_entries); + ut->add(ut, UT_TESTCASE, "map malloc error", test_map_malloc_error); +} + +/** + * Map 生成/破棄。 + * + * @process KcMap_new を実行する。。 + * @result KcMap が生成されること。 + * + * @process KcMap_delete にて Map を破棄する。 + * @result Map が破棄されること。 + */ +static void test_map_new(void) +{ + KcMap *map = KcMap_new(0); + assert_not_null(map); + KcMap_delete(map); + + map = KcMap_new(65538); + assert_not_null(map); + KcMap_delete(map); + + // map にデータが残った状態での削除 + map = KcMap_new(10); + assert_not_null(map); + map->put(map, "key1", "value1", 7); + KcMap_delete(map); +} + +/** + * Map への追加/取得。 + */ +static void test_map_put(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // 値取得 + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + size_t size; + const char *value = (const char *)map->get(map, keys[i], &size); + assert_equals(vals[i], value); + } + + // 存在しないキーの値取得 + void *ptr = map->get(map, "ABC", NULL); + assert_null(ptr); + + // キー上書き登録 + res = (char *)map->put(map, "key10", "abcdefg", strlen("abcdefg") + 1); + assert_equals("abcdefg", (const char *)res); + res = (char *)map->get(map, "key10", NULL); + assert_equals("abcdefg", (const char *)res); + + KcMap_delete(map); +} + +/** + * Map からの削除/サイズ確認。 + */ +static void test_map_remove(void) +{ + KcMap *map = KcMap_new(3); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + int size_counter = 14; + map->remove(map, "key15"); + size_t size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key15", NULL); + assert_null(res); + + map->remove(map, "key7"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key7", NULL); + assert_null(res); + + map->remove(map, "key13"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key13", NULL); + assert_null(res); + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + // 存在しないキー削除 + map->remove(map, "abc"); + size = map->size(map); + assert_equals(9, size); + + KcMap_delete(map); + + // パターン2 + map = KcMap_new(3); + assert_not_null(map); + + size_counter = 3; + for (int i = 0; i < size_counter; i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + size_counter--; + + map->remove(map, "key1"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key1", NULL); + assert_null(res); + + map->remove(map, "key2"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key2", NULL); + assert_null(res); + + map->remove(map, "key3"); + size = map->size(map); + assert_equals(size_counter, size); + size_counter--; + res = (char *)map->get(map, "key3", NULL); + assert_null(res); + + KcMap_delete(map); +} + +// エントリ +typedef struct UserData_ +{ + int value; +} UserData; +static bool test_map_entries_handler(const char *key, const void *val, size_t size, void *args) +{ + UserData *user_data = (UserData *)args; + user_data->value--; + printf("key=%-5s, value=%-5s, size=%zu\n", key, (const char *)val, size); + return (user_data->value >= 0); +} + +/** + * Map エントリ取得 + */ +static void test_map_entries(void) +{ + KcMap *map = KcMap_new(5); + assert_not_null(map); + + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + // entries + UserData args = { + .value = 10000}; + map->entries(map, test_map_entries_handler, &args); + + // entries [途中中断] + UserData args2 = { + .value = 5}; + map->entries(map, test_map_entries_handler, &args2); + + KcMap_delete(map); +} + +/** + * メモリ確保失敗 + */ +static void test_map_malloc_error(void) +{ + // 生成時失敗 + ut_alloc_control(0) + { + KcMap *map = KcMap_new(5); + assert_null(map); + } + + // put 時失敗 + KcMap *map = KcMap_new(5); + const char *keys[] = { + "key1", "key2", "key3", "key4", "key5", + "key6", "key7", "key8", "key9", "key10", + "key11", "key12", "key13", "key14", "key15"}; + const char *vals[] = { + "val1", "val2", "val3", "val4", "val5", + "val6", "val7", "val8", "val9", "val10", + "val11", "val12", "val13", "val14", "val15"}; + char *res; + for (int i = 0; i < (int)(sizeof(keys) / sizeof(const char *)); i++) + { + res = (char *)map->put(map, keys[i], vals[i], strlen(vals[i]) + 1); + assert_equals(vals[i], (const char *)res); + } + + ut_alloc_control(0) + { + res = (char *)map->put(map, "abc", "def", 4); + assert_null(res); + } + KcMap_delete(map); +} \ No newline at end of file diff --git a/modules/test/src/ut.c b/modules/test/src/ut.c index c84b7b9..b921513 100644 --- a/modules/test/src/ut.c +++ b/modules/test/src/ut.c @@ -9,9 +9,12 @@ { // UT Setup suite_assert(); + suite_dl(); + suite_env(); suite_list_array(); suite_list_linked(); suite_lock_guard(); + suite_map(); suite_memory_dump(); suite_memory_entry(); suite_memory_listener(); @@ -27,7 +30,10 @@ return 0; } -// メモリテスト用 +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、メモリ管理操作用 +// static int ut_can_alloc_counter = 0; static bool ut_can_alloc( KcMemoryEntry *entry, size_t alignment, size_t size, diff --git a/modules/test/src/ut.h b/modules/test/src/ut.h index 224e63b..de5fe82 100644 --- a/modules/test/src/ut.h +++ b/modules/test/src/ut.h @@ -5,9 +5,12 @@ #include extern void suite_assert(void); +extern void suite_dl(void); +extern void suite_env(void); extern void suite_list_array(void); extern void suite_list_linked(void); extern void suite_lock_guard(void); +extern void suite_map(void); extern void suite_memory_dump(void); extern void suite_memory_entry(void); extern void suite_memory_listener(void); @@ -15,6 +18,11 @@ extern void suite_memory(void); extern void suite_queue(void); +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、メモリ管理操作用 +// + extern bool (*_UT_KcMemory_can_alloc)( KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); diff --git a/modules/test/test-lib/Makefile b/modules/test/test-lib/Makefile new file mode 100644 index 0000000..df5830c --- /dev/null +++ b/modules/test/test-lib/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libtest + +ifeq ($(strip $(ARCH)),i686-w64-mingw32) +TARGET = $(NAME).dll +else +TARGET = $(NAME).so +endif + +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/test/test-lib/src/test.c b/modules/test/test-lib/src/test.c new file mode 100644 index 0000000..248eed9 --- /dev/null +++ b/modules/test/test-lib/src/test.c @@ -0,0 +1,21 @@ + +int test_add(int v1, int v2) +{ + return (v1 + v2); +} + +int test_sub(int v1, int v2) +{ + return (v1 - v2); +} + +int test_mul(int v1, int v2) +{ + return (v1 * v2); +} + +int test_div(int v1, int v2) +{ + return (v1 / v2); +} + diff --git a/modules/test/test.c b/modules/test/test.c new file mode 100644 index 0000000..d3325ea --- /dev/null +++ b/modules/test/test.c @@ -0,0 +1,8 @@ +#include + +FILE* output = stderr; +int main() +{ + fprintf(output, "TEST\n"); + return 0; +}