diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index fd0beab..99a171f 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,7 @@ * * @param size 要素のサイズ * @param cap リストの初期容量 - * @param file ファイル - * @param func 関数 - * @param line 行番号 + * @return ArrayList */ KcList *KcList_new_ArrayList(size_t size, int cap) { @@ -174,15 +173,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; bool is_contains = false; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { is_contains = true; break; @@ -213,12 +209,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; + // insert_index は常に 0 以上 int insert_index = (index < 0) ? info->size : index; bool is_success = true; kc_lock_guard(&(info->mutex)) { - is_success = ((0 <= insert_index) && (insert_index <= info->size)); - is_success = is_success && KcArrayList_increase_capacity(info); + is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info); element_type *info_data = (element_type *)info->data; if (is_success) { @@ -257,7 +253,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -323,7 +319,6 @@ * @param list 対象リスト * @param comparator リスト要素を比較するために使用される comparator * @param args comparator の第5引数に渡すオブジェクト - * @return true/false (ソート成功/ソート失敗) */ static void KcArrayList_sort(KcList *list, int (*comparator)(const void *element1, size_t size1, @@ -397,6 +392,10 @@ *size = info->element_size; } } + else + { + errno = EINVAL; + } } return res; } @@ -426,7 +425,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -465,15 +464,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; @@ -502,15 +498,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = (info->size - 1); idx >= 0; idx--) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index fd0beab..99a171f 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,7 @@ * * @param size 要素のサイズ * @param cap リストの初期容量 - * @param file ファイル - * @param func 関数 - * @param line 行番号 + * @return ArrayList */ KcList *KcList_new_ArrayList(size_t size, int cap) { @@ -174,15 +173,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; bool is_contains = false; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { is_contains = true; break; @@ -213,12 +209,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; + // insert_index は常に 0 以上 int insert_index = (index < 0) ? info->size : index; bool is_success = true; kc_lock_guard(&(info->mutex)) { - is_success = ((0 <= insert_index) && (insert_index <= info->size)); - is_success = is_success && KcArrayList_increase_capacity(info); + is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info); element_type *info_data = (element_type *)info->data; if (is_success) { @@ -257,7 +253,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -323,7 +319,6 @@ * @param list 対象リスト * @param comparator リスト要素を比較するために使用される comparator * @param args comparator の第5引数に渡すオブジェクト - * @return true/false (ソート成功/ソート失敗) */ static void KcArrayList_sort(KcList *list, int (*comparator)(const void *element1, size_t size1, @@ -397,6 +392,10 @@ *size = info->element_size; } } + else + { + errno = EINVAL; + } } return res; } @@ -426,7 +425,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -465,15 +464,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; @@ -502,15 +498,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = (info->size - 1); idx >= 0; idx--) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c new file mode 100644 index 0000000..9e746a1 --- /dev/null +++ b/modules/src/kc_list_linked.c @@ -0,0 +1,721 @@ +/** + * @file kc_list_linked.c + * @brief Linked リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * KcLinkedList Entry 情報 + */ +typedef struct KcLinkedListEntry_ +{ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcLinkedListEntry_ *next; //!< 次のエントリ + struct KcLinkedListEntry_ *prev; //!< 前のエントリ +} KcLinkedListEntry; + +/** + * KcLinkedList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t size; //!< エントリ数 + KcLinkedListEntry *head; //!< 先頭エントリ + KcLinkedListEntry *tail; //!< 末尾エントリ +} KcLinkedListInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcLinkedList_size(KcList *list); +static bool KcLinkedList_is_empty(KcList *list); +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size); +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size); +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size); +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_clear(KcList *list); +static void *KcLinkedList_get(KcList *list, int index, size_t *size); +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size); +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size); +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size); +static KcIterator *KcLinkedList_iterator(KcList *list, int index); +static void KcLinkedList_cleanup_info(KcList *list); + +// [内部関数用] +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index); +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size); + +// [内部関数用] For Quick Sort +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2); +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList *KcList_new_LinkedList(void) +{ + // KcLinkedList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | next --------+ + // | prev --------|--+ + // +--------------+ | | + // | |<-+ | + // | |<----+ + // +--------------+ + KcList *list = (KcList *)malloc( + sizeof(KcList) + sizeof(KcLinkedListInfo) + (sizeof(KcLinkedListEntry) * 2)); + if (list != NULL) + { + list->size = KcLinkedList_size; + list->is_empty = KcLinkedList_is_empty; + list->contains = KcLinkedList_contains; + list->add = KcLinkedList_add; + list->remove = KcLinkedList_remove; + list->sort = KcLinkedList_sort; + list->clear = KcLinkedList_clear; + list->get = KcLinkedList_get; + list->set = KcLinkedList_set; + list->index_of = KcLinkedList_index_of; + list->last_index_of = KcLinkedList_last_index_of; + list->iterator = KcLinkedList_iterator; + list->cleanup_info = KcLinkedList_cleanup_info; + list->_info = (list + 1); + + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->head = (KcLinkedListEntry *)(info + 1); + info->tail = info->head + 1; + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } + return list; +} + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcLinkedList_size(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcLinkedList_is_empty(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = info->head->next; + while (entry != info->tail) + { + if ((entry->size == size) && (memcmp(entry->value, element, size) == 0)) + { + is_contains = true; + break; + } + entry = entry->next; + } + } + return is_contains; +} + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * index に負値を指定した場合、末尾に要素を追加します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *insert_pos = KcLinkedList_search(info, index); + if (insert_pos != NULL) + { // エントリ作成 + KcLinkedListEntry *entry = KcLinkedList_new_entry(element, size); + if (entry != NULL) + { // リストに追加 (insert_pos の一つ前に追加) + info->size++; + entry->next = insert_pos; + entry->prev = insert_pos->prev; + insert_pos->prev->next = entry; + insert_pos->prev = entry; + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element と size が共に NULL でない場合、削除された要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のバッファサイズを指定します。また、削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + if ((element != NULL) && (size != NULL)) + { + size_t copy_size = (entry->size < *size) ? entry->size : *size; + memcpy(element, entry->value, copy_size); + *size = entry->size; + } + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + free(entry); + info->size--; + is_success = true; + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedList_sort_recur( + info, + info->head->next, + info->tail->prev, + comparator, args); + } +} + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcLinkedList_clear(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry; + entry = info->head->next; + while (entry != info->tail) + { + entry = entry->next; + free(entry->prev); + } + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } +} + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +static void *KcLinkedList_get(KcList *list, int index, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + + void *res = NULL; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + res = entry->value; + if (size) + { + *size = entry->size; + } + } + else + { + errno = EINVAL; + } + } + return res; +} + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element と org_size が共にNULL でない場合、置き換え前の要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。また、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *remove_entry = KcLinkedList_search(info, index); + if ((remove_entry != NULL) && (remove_entry != info->tail)) + { + KcLinkedListEntry *new_entry = KcLinkedList_new_entry(element, size); + if (new_entry) + { + if ((org_element != NULL) && (org_size != NULL)) + { + size_t copy_size = (remove_entry->size < *org_size) ? remove_entry->size : *org_size; + memcpy(org_element, remove_entry->value, copy_size); + *org_size = remove_entry->size; + } + // 削除する位置に新たなエントリを追加 + new_entry->next = remove_entry->next; + new_entry->prev = remove_entry->prev; + remove_entry->prev->next = new_entry; + remove_entry->next->prev = new_entry; + free(remove_entry); + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->head->next; entry != info->tail; entry = entry->next) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->tail->prev; entry != info->head; entry = entry->prev) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Iterator +// + +/** + * LinkedListe の Iterator 管理用構造体 + */ +typedef struct KcLinkedListIteratorEntry_ +{ + KcLinkedListInfo *info; //!< ArrayList情報 + KcLinkedListEntry *current; //!< 現在のエントリ +} KcLinkedListIteratorEntry; + +/** + * 次の要素があるか否かを返します。 + * + * @param ite Iterator + * @return true/false (次の要素が存在する/存在しない) + */ +static bool KcLinkedListIterator_hasNext(KcIterator *ite) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + return (entry->current != entry->info->tail); +} + +/** + * 次の要素を取得します。 + * size が指定されている場合、次の要素が size に格納されます。 + * 次の要素がない場合、NULL を返します。 + * + * @param ite Iterator + * @param size 要素のサイズ格納用 + * @return 次の要素 + */ +static const void *KcLinkedListIterator_next(KcIterator *ite, size_t *size) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + if (size != NULL) + { + *size = entry->current->size; + } + entry->current = entry->current->next; + return (entry->current->prev->value); +} + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +static KcIterator *KcLinkedList_iterator(KcList *list, int index) +{ + size_t ite_size = sizeof(KcIterator) + sizeof(KcLinkedListIteratorEntry); + KcIterator *ite = (KcIterator *)malloc(ite_size); + if (ite) + { + ite->hasNext = KcLinkedListIterator_hasNext; + ite->next = KcLinkedListIterator_next; + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)(((KcIterator *)ite) + 1); + ite->_info = entry; + + entry->info = list->_info; + entry->current = KcLinkedList_search(entry->info, index); + } + return ite; +} + +/** + * リスト情報をクリアします。 + * + * @param list 対象リスト + */ +static void KcLinkedList_cleanup_info(KcList *list) +{ + list->clear(list); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +/** + * [内部関数] リストより指定されたインデックスのエントリを取得します。 + * インデックスが -1 または、現在の size 位置の場合、 tail を返します。 + * インデックス位置が不正な場合、エラーコード EINVAL がセットされ NULL を返します。 + * + * @param info リスト情報 + * @param index インデックス + * @return エントリ + */ +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index) +{ + + KcLinkedListEntry *entry = NULL; + kc_lock_guard(&(info->mutex)) + { + if (index > (int)info->size) + { // インデックス不正のためエラーを設定 + errno = EINVAL; + } + else if ((index == -1) || (index == (int)info->size)) + { // インデックス -1 or 末尾のため、tail を返す。 + entry = info->tail; + } + else + { + int half_size = info->size / 2; + if (index < half_size) + { // 前半のindexのため、前方より検索 + entry = info->head; + for (int idx = 0; idx <= index; idx++) + { + entry = entry->next; + } + } + else + { // 後半のindexのため、後方より検索 + // head 0 1 2 3 tail + entry = info->tail; + for (int idx = info->size; idx > index; idx--) + { + entry = entry->prev; + } + } + } + } + return entry; +} + +/** + * 新たなエントリを生成します。 + * 生成に失敗した場合、errno に ENOMEM がセットされ、NULL が返されます。 + * + * @param value 値 + * @param size サイズ + * @return 生成したエントリ + */ +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size) +{ + size_t entry_size = size + sizeof(KcLinkedListEntry); + KcLinkedListEntry *entry = (KcLinkedListEntry *)malloc(entry_size); + if (entry != NULL) + { + entry->value = (entry + 1); + entry->size = size; + memcpy(entry->value, value, size); + } + else + { // メモリ確保失敗 + errno = ENOMEM; + } + return entry; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For Quick Sort +// + +/** + * 指定されたエントリを入れ替えます。 + * 同じエントリが渡された場合は何もしません。 + * + * @param entry1 入れ替えるエントリ1 + * @param entry2 入れ替えるエントリ2 + */ +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2) +{ + if (entry1 != entry2) + { + KcLinkedListEntry *temp = entry1->next; + entry1->next = entry2->next; + entry2->next = temp; + temp = entry1->prev; + entry1->prev = entry2->prev; + entry2->prev = temp; + } +} + +/** + * Quick Sort パーティション処理 + * + * @param info LinkedList 情報 + * @param low Low + * @param high High + * @param comparator 比較関数 + * @param args 比較関数に渡されるデータ + */ +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListEntry *pivot = high; + KcLinkedListEntry *i = low->prev; + + for (KcLinkedListEntry *j = low; j != high; j = j->next) + { + int ret = comparator( + pivot->value, pivot->size, j->value, j->size, args); + if (ret >= 0) + { // j <= pivot + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, j); + } + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, high); + } + return i; +} + +/** + * Quick Sort 再帰処理 + */ +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + if ((high != info->tail) && (low != high) && (low != high->next)) + { + KcLinkedListEntry *p = KcLinkedList_sort_partition(info, low, high, comparator, args); + KcLinkedList_sort_recur(info, low, p->prev, comparator, args); + KcLinkedList_sort_recur(info, p->next, high, comparator, args); + } +} diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index fd0beab..99a171f 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,7 @@ * * @param size 要素のサイズ * @param cap リストの初期容量 - * @param file ファイル - * @param func 関数 - * @param line 行番号 + * @return ArrayList */ KcList *KcList_new_ArrayList(size_t size, int cap) { @@ -174,15 +173,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; bool is_contains = false; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { is_contains = true; break; @@ -213,12 +209,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; + // insert_index は常に 0 以上 int insert_index = (index < 0) ? info->size : index; bool is_success = true; kc_lock_guard(&(info->mutex)) { - is_success = ((0 <= insert_index) && (insert_index <= info->size)); - is_success = is_success && KcArrayList_increase_capacity(info); + is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info); element_type *info_data = (element_type *)info->data; if (is_success) { @@ -257,7 +253,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -323,7 +319,6 @@ * @param list 対象リスト * @param comparator リスト要素を比較するために使用される comparator * @param args comparator の第5引数に渡すオブジェクト - * @return true/false (ソート成功/ソート失敗) */ static void KcArrayList_sort(KcList *list, int (*comparator)(const void *element1, size_t size1, @@ -397,6 +392,10 @@ *size = info->element_size; } } + else + { + errno = EINVAL; + } } return res; } @@ -426,7 +425,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -465,15 +464,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; @@ -502,15 +498,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = (info->size - 1); idx >= 0; idx--) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c new file mode 100644 index 0000000..9e746a1 --- /dev/null +++ b/modules/src/kc_list_linked.c @@ -0,0 +1,721 @@ +/** + * @file kc_list_linked.c + * @brief Linked リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * KcLinkedList Entry 情報 + */ +typedef struct KcLinkedListEntry_ +{ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcLinkedListEntry_ *next; //!< 次のエントリ + struct KcLinkedListEntry_ *prev; //!< 前のエントリ +} KcLinkedListEntry; + +/** + * KcLinkedList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t size; //!< エントリ数 + KcLinkedListEntry *head; //!< 先頭エントリ + KcLinkedListEntry *tail; //!< 末尾エントリ +} KcLinkedListInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcLinkedList_size(KcList *list); +static bool KcLinkedList_is_empty(KcList *list); +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size); +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size); +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size); +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_clear(KcList *list); +static void *KcLinkedList_get(KcList *list, int index, size_t *size); +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size); +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size); +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size); +static KcIterator *KcLinkedList_iterator(KcList *list, int index); +static void KcLinkedList_cleanup_info(KcList *list); + +// [内部関数用] +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index); +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size); + +// [内部関数用] For Quick Sort +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2); +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList *KcList_new_LinkedList(void) +{ + // KcLinkedList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | next --------+ + // | prev --------|--+ + // +--------------+ | | + // | |<-+ | + // | |<----+ + // +--------------+ + KcList *list = (KcList *)malloc( + sizeof(KcList) + sizeof(KcLinkedListInfo) + (sizeof(KcLinkedListEntry) * 2)); + if (list != NULL) + { + list->size = KcLinkedList_size; + list->is_empty = KcLinkedList_is_empty; + list->contains = KcLinkedList_contains; + list->add = KcLinkedList_add; + list->remove = KcLinkedList_remove; + list->sort = KcLinkedList_sort; + list->clear = KcLinkedList_clear; + list->get = KcLinkedList_get; + list->set = KcLinkedList_set; + list->index_of = KcLinkedList_index_of; + list->last_index_of = KcLinkedList_last_index_of; + list->iterator = KcLinkedList_iterator; + list->cleanup_info = KcLinkedList_cleanup_info; + list->_info = (list + 1); + + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->head = (KcLinkedListEntry *)(info + 1); + info->tail = info->head + 1; + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } + return list; +} + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcLinkedList_size(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcLinkedList_is_empty(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = info->head->next; + while (entry != info->tail) + { + if ((entry->size == size) && (memcmp(entry->value, element, size) == 0)) + { + is_contains = true; + break; + } + entry = entry->next; + } + } + return is_contains; +} + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * index に負値を指定した場合、末尾に要素を追加します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *insert_pos = KcLinkedList_search(info, index); + if (insert_pos != NULL) + { // エントリ作成 + KcLinkedListEntry *entry = KcLinkedList_new_entry(element, size); + if (entry != NULL) + { // リストに追加 (insert_pos の一つ前に追加) + info->size++; + entry->next = insert_pos; + entry->prev = insert_pos->prev; + insert_pos->prev->next = entry; + insert_pos->prev = entry; + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element と size が共に NULL でない場合、削除された要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のバッファサイズを指定します。また、削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + if ((element != NULL) && (size != NULL)) + { + size_t copy_size = (entry->size < *size) ? entry->size : *size; + memcpy(element, entry->value, copy_size); + *size = entry->size; + } + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + free(entry); + info->size--; + is_success = true; + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedList_sort_recur( + info, + info->head->next, + info->tail->prev, + comparator, args); + } +} + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcLinkedList_clear(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry; + entry = info->head->next; + while (entry != info->tail) + { + entry = entry->next; + free(entry->prev); + } + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } +} + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +static void *KcLinkedList_get(KcList *list, int index, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + + void *res = NULL; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + res = entry->value; + if (size) + { + *size = entry->size; + } + } + else + { + errno = EINVAL; + } + } + return res; +} + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element と org_size が共にNULL でない場合、置き換え前の要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。また、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *remove_entry = KcLinkedList_search(info, index); + if ((remove_entry != NULL) && (remove_entry != info->tail)) + { + KcLinkedListEntry *new_entry = KcLinkedList_new_entry(element, size); + if (new_entry) + { + if ((org_element != NULL) && (org_size != NULL)) + { + size_t copy_size = (remove_entry->size < *org_size) ? remove_entry->size : *org_size; + memcpy(org_element, remove_entry->value, copy_size); + *org_size = remove_entry->size; + } + // 削除する位置に新たなエントリを追加 + new_entry->next = remove_entry->next; + new_entry->prev = remove_entry->prev; + remove_entry->prev->next = new_entry; + remove_entry->next->prev = new_entry; + free(remove_entry); + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->head->next; entry != info->tail; entry = entry->next) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->tail->prev; entry != info->head; entry = entry->prev) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Iterator +// + +/** + * LinkedListe の Iterator 管理用構造体 + */ +typedef struct KcLinkedListIteratorEntry_ +{ + KcLinkedListInfo *info; //!< ArrayList情報 + KcLinkedListEntry *current; //!< 現在のエントリ +} KcLinkedListIteratorEntry; + +/** + * 次の要素があるか否かを返します。 + * + * @param ite Iterator + * @return true/false (次の要素が存在する/存在しない) + */ +static bool KcLinkedListIterator_hasNext(KcIterator *ite) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + return (entry->current != entry->info->tail); +} + +/** + * 次の要素を取得します。 + * size が指定されている場合、次の要素が size に格納されます。 + * 次の要素がない場合、NULL を返します。 + * + * @param ite Iterator + * @param size 要素のサイズ格納用 + * @return 次の要素 + */ +static const void *KcLinkedListIterator_next(KcIterator *ite, size_t *size) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + if (size != NULL) + { + *size = entry->current->size; + } + entry->current = entry->current->next; + return (entry->current->prev->value); +} + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +static KcIterator *KcLinkedList_iterator(KcList *list, int index) +{ + size_t ite_size = sizeof(KcIterator) + sizeof(KcLinkedListIteratorEntry); + KcIterator *ite = (KcIterator *)malloc(ite_size); + if (ite) + { + ite->hasNext = KcLinkedListIterator_hasNext; + ite->next = KcLinkedListIterator_next; + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)(((KcIterator *)ite) + 1); + ite->_info = entry; + + entry->info = list->_info; + entry->current = KcLinkedList_search(entry->info, index); + } + return ite; +} + +/** + * リスト情報をクリアします。 + * + * @param list 対象リスト + */ +static void KcLinkedList_cleanup_info(KcList *list) +{ + list->clear(list); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +/** + * [内部関数] リストより指定されたインデックスのエントリを取得します。 + * インデックスが -1 または、現在の size 位置の場合、 tail を返します。 + * インデックス位置が不正な場合、エラーコード EINVAL がセットされ NULL を返します。 + * + * @param info リスト情報 + * @param index インデックス + * @return エントリ + */ +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index) +{ + + KcLinkedListEntry *entry = NULL; + kc_lock_guard(&(info->mutex)) + { + if (index > (int)info->size) + { // インデックス不正のためエラーを設定 + errno = EINVAL; + } + else if ((index == -1) || (index == (int)info->size)) + { // インデックス -1 or 末尾のため、tail を返す。 + entry = info->tail; + } + else + { + int half_size = info->size / 2; + if (index < half_size) + { // 前半のindexのため、前方より検索 + entry = info->head; + for (int idx = 0; idx <= index; idx++) + { + entry = entry->next; + } + } + else + { // 後半のindexのため、後方より検索 + // head 0 1 2 3 tail + entry = info->tail; + for (int idx = info->size; idx > index; idx--) + { + entry = entry->prev; + } + } + } + } + return entry; +} + +/** + * 新たなエントリを生成します。 + * 生成に失敗した場合、errno に ENOMEM がセットされ、NULL が返されます。 + * + * @param value 値 + * @param size サイズ + * @return 生成したエントリ + */ +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size) +{ + size_t entry_size = size + sizeof(KcLinkedListEntry); + KcLinkedListEntry *entry = (KcLinkedListEntry *)malloc(entry_size); + if (entry != NULL) + { + entry->value = (entry + 1); + entry->size = size; + memcpy(entry->value, value, size); + } + else + { // メモリ確保失敗 + errno = ENOMEM; + } + return entry; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For Quick Sort +// + +/** + * 指定されたエントリを入れ替えます。 + * 同じエントリが渡された場合は何もしません。 + * + * @param entry1 入れ替えるエントリ1 + * @param entry2 入れ替えるエントリ2 + */ +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2) +{ + if (entry1 != entry2) + { + KcLinkedListEntry *temp = entry1->next; + entry1->next = entry2->next; + entry2->next = temp; + temp = entry1->prev; + entry1->prev = entry2->prev; + entry2->prev = temp; + } +} + +/** + * Quick Sort パーティション処理 + * + * @param info LinkedList 情報 + * @param low Low + * @param high High + * @param comparator 比較関数 + * @param args 比較関数に渡されるデータ + */ +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListEntry *pivot = high; + KcLinkedListEntry *i = low->prev; + + for (KcLinkedListEntry *j = low; j != high; j = j->next) + { + int ret = comparator( + pivot->value, pivot->size, j->value, j->size, args); + if (ret >= 0) + { // j <= pivot + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, j); + } + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, high); + } + return i; +} + +/** + * Quick Sort 再帰処理 + */ +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + if ((high != info->tail) && (low != high) && (low != high->next)) + { + KcLinkedListEntry *p = KcLinkedList_sort_partition(info, low, high, comparator, args); + KcLinkedList_sort_recur(info, low, p->prev, comparator, args); + KcLinkedList_sort_recur(info, p->next, high, comparator, args); + } +} diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 3b807c0..5cc4186 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -24,7 +24,9 @@ // プロトタイプ宣言 // --- KcMemory -static void KcMemory_dump_leak(void); +// 確保中のメモリダンプは、 KcMemory_dump にて実施可能であり、 +// メモリリーク情報をダンプする KcMemory_dump_leak は明示的に公開はしない。 +void KcMemory_dump_leak(void); static bool KcMemory_print(const char *msg); // --- KcMemoryManager @@ -47,10 +49,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); static void *KcMemoryManager_reallocate_managed_ptr(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); static void KcMemoryManager_deallocate(void *ptr, KcMemoryMark expected_mark); //////////////////////////////////////////////////////////////////////////////// @@ -80,8 +78,6 @@ ._allocate = KcMemoryManager_allocate, ._reallocate = KcMemoryManager_reallocate, ._reallocate_managed_ptr = KcMemoryManager_reallocate_managed_ptr, - ._reallocate_invalid_ptr = KcMemoryManager_reallocate_invalid_ptr, - ._reallocate_unmanaged_ptr = KcMemoryManager_reallocate_unmanaged_ptr, ._deallocate = KcMemoryManager_deallocate, // --- 変数 --- @@ -116,7 +112,12 @@ void KcMemory_start(bool detail) { kc_memory_manager->_init(); - atexit(KcMemory_dump_leak); + static bool KcMemory_start = false; + if (!KcMemory_start) + { // atexit への登録は一度だけとする。 + atexit(KcMemory_dump_leak); + KcMemory_start = true; + } if (detail) { KcMemory_listener.allocate = KcMemoryListener_dump_allocate; @@ -142,7 +143,7 @@ /** * メモリリークしているメモリ情報をダンプします。 */ -static void KcMemory_dump_leak(void) +void KcMemory_dump_leak(void) { if (kc_memory_manager->_head._next != &(kc_memory_manager->_tail)) { @@ -316,7 +317,7 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 @@ -519,23 +520,9 @@ void *data_ptr = NULL; KcMemoryEntry *entry = (KcMemoryEntry *)ptr; entry--; - switch (entry->mark) + if (entry->mark == KC_MEMORY_ALLOCATED) { - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate - data_ptr = kc_memory_manager->_allocate(0, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED: // 管理されたメモリの realloc data_ptr = kc_memory_manager->_reallocate_managed_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - default: // 管理外メモリの realloc - data_ptr = kc_memory_manager->_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); - break; } return data_ptr; } @@ -568,7 +555,7 @@ KcMemoryEntry *new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); if (new_entry != NULL) { // メモリ確保成功 - kc_memory_manager->_listener.free(entry); + kc_memory_manager->_listener.free(new_entry); kc_memory_manager->_add(new_entry); kc_memory_manager->_listener.allocate(new_entry); data_ptr = new_entry->data; @@ -585,76 +572,6 @@ } // ------------------------------------- -// _reallocate_invalid_ptr -// ------------------------------------- -/** - * [内部利用関数] - * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - UNUSED_VARIABLE(ptr); - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate (invalid pointer)\n"); - errno = EINVAL; - return NULL; -} - -// ------------------------------------- -// _reallocate_unmanaged_ptr -// ------------------------------------- -/** - * [内部利用関数] - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - // | - // +----------+-------------------+ - // | 元の領域 | 追加分 + 管理領域 | - // +----------+-------------------+ - // ↓ - // ↓ memmove で 元の領域 + 追加分を、 - // ↓ 管理領域分を確保した先にコピーする - // ↓ - // +----------+----------+--------+ - // | 管理領域 | 元の領域 | 追加分 | - // +----------+----------+--------+ - void *data_ptr = NULL; - KcMemoryEntry *new_entry = KcMemoryEntry_new(ptr, 0, size, mark, file, func, line); - if (new_entry != NULL) - { // メモリ確保成功 - // memmove で 元の領域 + 追加分 をコピーして、メモリエントリとして追加する。 - memmove((new_entry + 1), new_entry, size); - kc_memory_manager->_add(new_entry); - kc_memory_manager->_listener.allocate(new_entry); - data_ptr = new_entry->data; - } - else - { // メモリ確保失敗 => エラー通知する。 - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate\n"); - } - return data_ptr; -} - -// ------------------------------------- // _deallocate // ------------------------------------- /** @@ -680,33 +597,64 @@ KcMemoryEntry_delete(entry); } else - { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 - switch (entry->mark) - { - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - case KC_MEMORY_ALLOCATED: // malloc 等で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use free)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete[])\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - default: // 管理外メモリ - free(ptr); - break; - } + { // 期待通りでない場合、管理外メモリのため stdlib.h の free にて解放する。 + raw_free(ptr); } } + +/** + * stdlib.h の malloc。 + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_malloc(size_t size) +{ + return malloc(size); +} + +/** + * stdlib.h の aligned_alloc。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} + +/** + * stdlib.h の calloc。 + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ +void *raw_calloc(size_t nmemb, size_t size) +{ + return calloc(nmemb, size); +} + +/** + * stdlib.h の realloc。 + * + * @param ptr メモリを再確保するポインタ + * @param size 確保しなおすメモリサイズ + * @return メモリへのポインタ + */ +void *raw_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +/** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ +void raw_free(void *ptr) +{ + free(ptr); +} diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index fd0beab..99a171f 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,7 @@ * * @param size 要素のサイズ * @param cap リストの初期容量 - * @param file ファイル - * @param func 関数 - * @param line 行番号 + * @return ArrayList */ KcList *KcList_new_ArrayList(size_t size, int cap) { @@ -174,15 +173,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; bool is_contains = false; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { is_contains = true; break; @@ -213,12 +209,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; + // insert_index は常に 0 以上 int insert_index = (index < 0) ? info->size : index; bool is_success = true; kc_lock_guard(&(info->mutex)) { - is_success = ((0 <= insert_index) && (insert_index <= info->size)); - is_success = is_success && KcArrayList_increase_capacity(info); + is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info); element_type *info_data = (element_type *)info->data; if (is_success) { @@ -257,7 +253,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -323,7 +319,6 @@ * @param list 対象リスト * @param comparator リスト要素を比較するために使用される comparator * @param args comparator の第5引数に渡すオブジェクト - * @return true/false (ソート成功/ソート失敗) */ static void KcArrayList_sort(KcList *list, int (*comparator)(const void *element1, size_t size1, @@ -397,6 +392,10 @@ *size = info->element_size; } } + else + { + errno = EINVAL; + } } return res; } @@ -426,7 +425,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -465,15 +464,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; @@ -502,15 +498,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = (info->size - 1); idx >= 0; idx--) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c new file mode 100644 index 0000000..9e746a1 --- /dev/null +++ b/modules/src/kc_list_linked.c @@ -0,0 +1,721 @@ +/** + * @file kc_list_linked.c + * @brief Linked リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * KcLinkedList Entry 情報 + */ +typedef struct KcLinkedListEntry_ +{ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcLinkedListEntry_ *next; //!< 次のエントリ + struct KcLinkedListEntry_ *prev; //!< 前のエントリ +} KcLinkedListEntry; + +/** + * KcLinkedList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t size; //!< エントリ数 + KcLinkedListEntry *head; //!< 先頭エントリ + KcLinkedListEntry *tail; //!< 末尾エントリ +} KcLinkedListInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcLinkedList_size(KcList *list); +static bool KcLinkedList_is_empty(KcList *list); +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size); +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size); +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size); +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_clear(KcList *list); +static void *KcLinkedList_get(KcList *list, int index, size_t *size); +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size); +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size); +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size); +static KcIterator *KcLinkedList_iterator(KcList *list, int index); +static void KcLinkedList_cleanup_info(KcList *list); + +// [内部関数用] +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index); +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size); + +// [内部関数用] For Quick Sort +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2); +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList *KcList_new_LinkedList(void) +{ + // KcLinkedList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | next --------+ + // | prev --------|--+ + // +--------------+ | | + // | |<-+ | + // | |<----+ + // +--------------+ + KcList *list = (KcList *)malloc( + sizeof(KcList) + sizeof(KcLinkedListInfo) + (sizeof(KcLinkedListEntry) * 2)); + if (list != NULL) + { + list->size = KcLinkedList_size; + list->is_empty = KcLinkedList_is_empty; + list->contains = KcLinkedList_contains; + list->add = KcLinkedList_add; + list->remove = KcLinkedList_remove; + list->sort = KcLinkedList_sort; + list->clear = KcLinkedList_clear; + list->get = KcLinkedList_get; + list->set = KcLinkedList_set; + list->index_of = KcLinkedList_index_of; + list->last_index_of = KcLinkedList_last_index_of; + list->iterator = KcLinkedList_iterator; + list->cleanup_info = KcLinkedList_cleanup_info; + list->_info = (list + 1); + + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->head = (KcLinkedListEntry *)(info + 1); + info->tail = info->head + 1; + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } + return list; +} + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcLinkedList_size(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcLinkedList_is_empty(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = info->head->next; + while (entry != info->tail) + { + if ((entry->size == size) && (memcmp(entry->value, element, size) == 0)) + { + is_contains = true; + break; + } + entry = entry->next; + } + } + return is_contains; +} + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * index に負値を指定した場合、末尾に要素を追加します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *insert_pos = KcLinkedList_search(info, index); + if (insert_pos != NULL) + { // エントリ作成 + KcLinkedListEntry *entry = KcLinkedList_new_entry(element, size); + if (entry != NULL) + { // リストに追加 (insert_pos の一つ前に追加) + info->size++; + entry->next = insert_pos; + entry->prev = insert_pos->prev; + insert_pos->prev->next = entry; + insert_pos->prev = entry; + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element と size が共に NULL でない場合、削除された要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のバッファサイズを指定します。また、削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + if ((element != NULL) && (size != NULL)) + { + size_t copy_size = (entry->size < *size) ? entry->size : *size; + memcpy(element, entry->value, copy_size); + *size = entry->size; + } + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + free(entry); + info->size--; + is_success = true; + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedList_sort_recur( + info, + info->head->next, + info->tail->prev, + comparator, args); + } +} + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcLinkedList_clear(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry; + entry = info->head->next; + while (entry != info->tail) + { + entry = entry->next; + free(entry->prev); + } + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } +} + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +static void *KcLinkedList_get(KcList *list, int index, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + + void *res = NULL; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + res = entry->value; + if (size) + { + *size = entry->size; + } + } + else + { + errno = EINVAL; + } + } + return res; +} + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element と org_size が共にNULL でない場合、置き換え前の要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。また、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *remove_entry = KcLinkedList_search(info, index); + if ((remove_entry != NULL) && (remove_entry != info->tail)) + { + KcLinkedListEntry *new_entry = KcLinkedList_new_entry(element, size); + if (new_entry) + { + if ((org_element != NULL) && (org_size != NULL)) + { + size_t copy_size = (remove_entry->size < *org_size) ? remove_entry->size : *org_size; + memcpy(org_element, remove_entry->value, copy_size); + *org_size = remove_entry->size; + } + // 削除する位置に新たなエントリを追加 + new_entry->next = remove_entry->next; + new_entry->prev = remove_entry->prev; + remove_entry->prev->next = new_entry; + remove_entry->next->prev = new_entry; + free(remove_entry); + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->head->next; entry != info->tail; entry = entry->next) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->tail->prev; entry != info->head; entry = entry->prev) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Iterator +// + +/** + * LinkedListe の Iterator 管理用構造体 + */ +typedef struct KcLinkedListIteratorEntry_ +{ + KcLinkedListInfo *info; //!< ArrayList情報 + KcLinkedListEntry *current; //!< 現在のエントリ +} KcLinkedListIteratorEntry; + +/** + * 次の要素があるか否かを返します。 + * + * @param ite Iterator + * @return true/false (次の要素が存在する/存在しない) + */ +static bool KcLinkedListIterator_hasNext(KcIterator *ite) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + return (entry->current != entry->info->tail); +} + +/** + * 次の要素を取得します。 + * size が指定されている場合、次の要素が size に格納されます。 + * 次の要素がない場合、NULL を返します。 + * + * @param ite Iterator + * @param size 要素のサイズ格納用 + * @return 次の要素 + */ +static const void *KcLinkedListIterator_next(KcIterator *ite, size_t *size) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + if (size != NULL) + { + *size = entry->current->size; + } + entry->current = entry->current->next; + return (entry->current->prev->value); +} + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +static KcIterator *KcLinkedList_iterator(KcList *list, int index) +{ + size_t ite_size = sizeof(KcIterator) + sizeof(KcLinkedListIteratorEntry); + KcIterator *ite = (KcIterator *)malloc(ite_size); + if (ite) + { + ite->hasNext = KcLinkedListIterator_hasNext; + ite->next = KcLinkedListIterator_next; + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)(((KcIterator *)ite) + 1); + ite->_info = entry; + + entry->info = list->_info; + entry->current = KcLinkedList_search(entry->info, index); + } + return ite; +} + +/** + * リスト情報をクリアします。 + * + * @param list 対象リスト + */ +static void KcLinkedList_cleanup_info(KcList *list) +{ + list->clear(list); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +/** + * [内部関数] リストより指定されたインデックスのエントリを取得します。 + * インデックスが -1 または、現在の size 位置の場合、 tail を返します。 + * インデックス位置が不正な場合、エラーコード EINVAL がセットされ NULL を返します。 + * + * @param info リスト情報 + * @param index インデックス + * @return エントリ + */ +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index) +{ + + KcLinkedListEntry *entry = NULL; + kc_lock_guard(&(info->mutex)) + { + if (index > (int)info->size) + { // インデックス不正のためエラーを設定 + errno = EINVAL; + } + else if ((index == -1) || (index == (int)info->size)) + { // インデックス -1 or 末尾のため、tail を返す。 + entry = info->tail; + } + else + { + int half_size = info->size / 2; + if (index < half_size) + { // 前半のindexのため、前方より検索 + entry = info->head; + for (int idx = 0; idx <= index; idx++) + { + entry = entry->next; + } + } + else + { // 後半のindexのため、後方より検索 + // head 0 1 2 3 tail + entry = info->tail; + for (int idx = info->size; idx > index; idx--) + { + entry = entry->prev; + } + } + } + } + return entry; +} + +/** + * 新たなエントリを生成します。 + * 生成に失敗した場合、errno に ENOMEM がセットされ、NULL が返されます。 + * + * @param value 値 + * @param size サイズ + * @return 生成したエントリ + */ +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size) +{ + size_t entry_size = size + sizeof(KcLinkedListEntry); + KcLinkedListEntry *entry = (KcLinkedListEntry *)malloc(entry_size); + if (entry != NULL) + { + entry->value = (entry + 1); + entry->size = size; + memcpy(entry->value, value, size); + } + else + { // メモリ確保失敗 + errno = ENOMEM; + } + return entry; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For Quick Sort +// + +/** + * 指定されたエントリを入れ替えます。 + * 同じエントリが渡された場合は何もしません。 + * + * @param entry1 入れ替えるエントリ1 + * @param entry2 入れ替えるエントリ2 + */ +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2) +{ + if (entry1 != entry2) + { + KcLinkedListEntry *temp = entry1->next; + entry1->next = entry2->next; + entry2->next = temp; + temp = entry1->prev; + entry1->prev = entry2->prev; + entry2->prev = temp; + } +} + +/** + * Quick Sort パーティション処理 + * + * @param info LinkedList 情報 + * @param low Low + * @param high High + * @param comparator 比較関数 + * @param args 比較関数に渡されるデータ + */ +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListEntry *pivot = high; + KcLinkedListEntry *i = low->prev; + + for (KcLinkedListEntry *j = low; j != high; j = j->next) + { + int ret = comparator( + pivot->value, pivot->size, j->value, j->size, args); + if (ret >= 0) + { // j <= pivot + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, j); + } + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, high); + } + return i; +} + +/** + * Quick Sort 再帰処理 + */ +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + if ((high != info->tail) && (low != high) && (low != high->next)) + { + KcLinkedListEntry *p = KcLinkedList_sort_partition(info, low, high, comparator, args); + KcLinkedList_sort_recur(info, low, p->prev, comparator, args); + KcLinkedList_sort_recur(info, p->next, high, comparator, args); + } +} diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 3b807c0..5cc4186 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -24,7 +24,9 @@ // プロトタイプ宣言 // --- KcMemory -static void KcMemory_dump_leak(void); +// 確保中のメモリダンプは、 KcMemory_dump にて実施可能であり、 +// メモリリーク情報をダンプする KcMemory_dump_leak は明示的に公開はしない。 +void KcMemory_dump_leak(void); static bool KcMemory_print(const char *msg); // --- KcMemoryManager @@ -47,10 +49,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); static void *KcMemoryManager_reallocate_managed_ptr(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); static void KcMemoryManager_deallocate(void *ptr, KcMemoryMark expected_mark); //////////////////////////////////////////////////////////////////////////////// @@ -80,8 +78,6 @@ ._allocate = KcMemoryManager_allocate, ._reallocate = KcMemoryManager_reallocate, ._reallocate_managed_ptr = KcMemoryManager_reallocate_managed_ptr, - ._reallocate_invalid_ptr = KcMemoryManager_reallocate_invalid_ptr, - ._reallocate_unmanaged_ptr = KcMemoryManager_reallocate_unmanaged_ptr, ._deallocate = KcMemoryManager_deallocate, // --- 変数 --- @@ -116,7 +112,12 @@ void KcMemory_start(bool detail) { kc_memory_manager->_init(); - atexit(KcMemory_dump_leak); + static bool KcMemory_start = false; + if (!KcMemory_start) + { // atexit への登録は一度だけとする。 + atexit(KcMemory_dump_leak); + KcMemory_start = true; + } if (detail) { KcMemory_listener.allocate = KcMemoryListener_dump_allocate; @@ -142,7 +143,7 @@ /** * メモリリークしているメモリ情報をダンプします。 */ -static void KcMemory_dump_leak(void) +void KcMemory_dump_leak(void) { if (kc_memory_manager->_head._next != &(kc_memory_manager->_tail)) { @@ -316,7 +317,7 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 @@ -519,23 +520,9 @@ void *data_ptr = NULL; KcMemoryEntry *entry = (KcMemoryEntry *)ptr; entry--; - switch (entry->mark) + if (entry->mark == KC_MEMORY_ALLOCATED) { - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate - data_ptr = kc_memory_manager->_allocate(0, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED: // 管理されたメモリの realloc data_ptr = kc_memory_manager->_reallocate_managed_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - default: // 管理外メモリの realloc - data_ptr = kc_memory_manager->_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); - break; } return data_ptr; } @@ -568,7 +555,7 @@ KcMemoryEntry *new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); if (new_entry != NULL) { // メモリ確保成功 - kc_memory_manager->_listener.free(entry); + kc_memory_manager->_listener.free(new_entry); kc_memory_manager->_add(new_entry); kc_memory_manager->_listener.allocate(new_entry); data_ptr = new_entry->data; @@ -585,76 +572,6 @@ } // ------------------------------------- -// _reallocate_invalid_ptr -// ------------------------------------- -/** - * [内部利用関数] - * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - UNUSED_VARIABLE(ptr); - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate (invalid pointer)\n"); - errno = EINVAL; - return NULL; -} - -// ------------------------------------- -// _reallocate_unmanaged_ptr -// ------------------------------------- -/** - * [内部利用関数] - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - // | - // +----------+-------------------+ - // | 元の領域 | 追加分 + 管理領域 | - // +----------+-------------------+ - // ↓ - // ↓ memmove で 元の領域 + 追加分を、 - // ↓ 管理領域分を確保した先にコピーする - // ↓ - // +----------+----------+--------+ - // | 管理領域 | 元の領域 | 追加分 | - // +----------+----------+--------+ - void *data_ptr = NULL; - KcMemoryEntry *new_entry = KcMemoryEntry_new(ptr, 0, size, mark, file, func, line); - if (new_entry != NULL) - { // メモリ確保成功 - // memmove で 元の領域 + 追加分 をコピーして、メモリエントリとして追加する。 - memmove((new_entry + 1), new_entry, size); - kc_memory_manager->_add(new_entry); - kc_memory_manager->_listener.allocate(new_entry); - data_ptr = new_entry->data; - } - else - { // メモリ確保失敗 => エラー通知する。 - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate\n"); - } - return data_ptr; -} - -// ------------------------------------- // _deallocate // ------------------------------------- /** @@ -680,33 +597,64 @@ KcMemoryEntry_delete(entry); } else - { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 - switch (entry->mark) - { - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - case KC_MEMORY_ALLOCATED: // malloc 等で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use free)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete[])\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - default: // 管理外メモリ - free(ptr); - break; - } + { // 期待通りでない場合、管理外メモリのため stdlib.h の free にて解放する。 + raw_free(ptr); } } + +/** + * stdlib.h の malloc。 + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_malloc(size_t size) +{ + return malloc(size); +} + +/** + * stdlib.h の aligned_alloc。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} + +/** + * stdlib.h の calloc。 + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ +void *raw_calloc(size_t nmemb, size_t size) +{ + return calloc(nmemb, size); +} + +/** + * stdlib.h の realloc。 + * + * @param ptr メモリを再確保するポインタ + * @param size 確保しなおすメモリサイズ + * @return メモリへのポインタ + */ +void *raw_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +/** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ +void raw_free(void *ptr) +{ + free(ptr); +} diff --git a/modules/src/kc_memory_entry.c b/modules/src/kc_memory_entry.c index b855508..85f3fc2 100644 --- a/modules/src/kc_memory_entry.c +++ b/modules/src/kc_memory_entry.c @@ -5,9 +5,14 @@ */ #include +#include #include #include "kc_memory_entry_inner.h" +#ifdef KC_MEMORY_ENABLED +#undef KC_MEMORY_ENABLED +#endif + //////////////////////////////////////////////////////////////////////////////// // // 定数定義 @@ -16,6 +21,18 @@ /** パディング */ #define KC_MEMORY_PADDING (sizeof(void *) * 2) +//////////////////////////////////////////////////////////////////////////////// +// +// 変数定義 +// + +// For UNITTEST +#ifdef UNITTEST +bool (*_UT_KcMemory_can_alloc)( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) = NULL; +#endif // UNITTEST + /** * KcMemoryEntry を構築します。 * entry が NULL の場合、新規に KeMemoryEntry を構築します。 @@ -34,15 +51,22 @@ KcMemoryEntry *KcMemoryEntry_new(KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line) { +#ifdef UNITTEST + if (_UT_KcMemory_can_alloc && + !_UT_KcMemory_can_alloc(entry, alignment, size, mark, file, func, line)) + { + return NULL; + } +#endif KcMemoryEntry *new_entry; if ((entry == NULL) && (alignment > 0)) { // アライメント指定でメモリを確保する。 - new_entry = (KcMemoryEntry *)aligned_alloc(alignment, + new_entry = (KcMemoryEntry *)raw_aligned_alloc(alignment, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } else { - new_entry = (KcMemoryEntry *)realloc(entry, + new_entry = (KcMemoryEntry *)raw_realloc(entry, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } @@ -59,7 +83,7 @@ { entry->mark = KC_MEMORY_DELETED; entry->size = 0; - free(entry); + raw_free(entry); } /** diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index fd0beab..99a171f 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,7 @@ * * @param size 要素のサイズ * @param cap リストの初期容量 - * @param file ファイル - * @param func 関数 - * @param line 行番号 + * @return ArrayList */ KcList *KcList_new_ArrayList(size_t size, int cap) { @@ -174,15 +173,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; bool is_contains = false; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { is_contains = true; break; @@ -213,12 +209,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; + // insert_index は常に 0 以上 int insert_index = (index < 0) ? info->size : index; bool is_success = true; kc_lock_guard(&(info->mutex)) { - is_success = ((0 <= insert_index) && (insert_index <= info->size)); - is_success = is_success && KcArrayList_increase_capacity(info); + is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info); element_type *info_data = (element_type *)info->data; if (is_success) { @@ -257,7 +253,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -323,7 +319,6 @@ * @param list 対象リスト * @param comparator リスト要素を比較するために使用される comparator * @param args comparator の第5引数に渡すオブジェクト - * @return true/false (ソート成功/ソート失敗) */ static void KcArrayList_sort(KcList *list, int (*comparator)(const void *element1, size_t size1, @@ -397,6 +392,10 @@ *size = info->element_size; } } + else + { + errno = EINVAL; + } } return res; } @@ -426,7 +425,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -465,15 +464,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; @@ -502,15 +498,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = (info->size - 1); idx >= 0; idx--) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c new file mode 100644 index 0000000..9e746a1 --- /dev/null +++ b/modules/src/kc_list_linked.c @@ -0,0 +1,721 @@ +/** + * @file kc_list_linked.c + * @brief Linked リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * KcLinkedList Entry 情報 + */ +typedef struct KcLinkedListEntry_ +{ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcLinkedListEntry_ *next; //!< 次のエントリ + struct KcLinkedListEntry_ *prev; //!< 前のエントリ +} KcLinkedListEntry; + +/** + * KcLinkedList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t size; //!< エントリ数 + KcLinkedListEntry *head; //!< 先頭エントリ + KcLinkedListEntry *tail; //!< 末尾エントリ +} KcLinkedListInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcLinkedList_size(KcList *list); +static bool KcLinkedList_is_empty(KcList *list); +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size); +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size); +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size); +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_clear(KcList *list); +static void *KcLinkedList_get(KcList *list, int index, size_t *size); +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size); +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size); +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size); +static KcIterator *KcLinkedList_iterator(KcList *list, int index); +static void KcLinkedList_cleanup_info(KcList *list); + +// [内部関数用] +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index); +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size); + +// [内部関数用] For Quick Sort +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2); +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList *KcList_new_LinkedList(void) +{ + // KcLinkedList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | next --------+ + // | prev --------|--+ + // +--------------+ | | + // | |<-+ | + // | |<----+ + // +--------------+ + KcList *list = (KcList *)malloc( + sizeof(KcList) + sizeof(KcLinkedListInfo) + (sizeof(KcLinkedListEntry) * 2)); + if (list != NULL) + { + list->size = KcLinkedList_size; + list->is_empty = KcLinkedList_is_empty; + list->contains = KcLinkedList_contains; + list->add = KcLinkedList_add; + list->remove = KcLinkedList_remove; + list->sort = KcLinkedList_sort; + list->clear = KcLinkedList_clear; + list->get = KcLinkedList_get; + list->set = KcLinkedList_set; + list->index_of = KcLinkedList_index_of; + list->last_index_of = KcLinkedList_last_index_of; + list->iterator = KcLinkedList_iterator; + list->cleanup_info = KcLinkedList_cleanup_info; + list->_info = (list + 1); + + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->head = (KcLinkedListEntry *)(info + 1); + info->tail = info->head + 1; + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } + return list; +} + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcLinkedList_size(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcLinkedList_is_empty(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = info->head->next; + while (entry != info->tail) + { + if ((entry->size == size) && (memcmp(entry->value, element, size) == 0)) + { + is_contains = true; + break; + } + entry = entry->next; + } + } + return is_contains; +} + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * index に負値を指定した場合、末尾に要素を追加します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *insert_pos = KcLinkedList_search(info, index); + if (insert_pos != NULL) + { // エントリ作成 + KcLinkedListEntry *entry = KcLinkedList_new_entry(element, size); + if (entry != NULL) + { // リストに追加 (insert_pos の一つ前に追加) + info->size++; + entry->next = insert_pos; + entry->prev = insert_pos->prev; + insert_pos->prev->next = entry; + insert_pos->prev = entry; + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element と size が共に NULL でない場合、削除された要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のバッファサイズを指定します。また、削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + if ((element != NULL) && (size != NULL)) + { + size_t copy_size = (entry->size < *size) ? entry->size : *size; + memcpy(element, entry->value, copy_size); + *size = entry->size; + } + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + free(entry); + info->size--; + is_success = true; + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedList_sort_recur( + info, + info->head->next, + info->tail->prev, + comparator, args); + } +} + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcLinkedList_clear(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry; + entry = info->head->next; + while (entry != info->tail) + { + entry = entry->next; + free(entry->prev); + } + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } +} + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +static void *KcLinkedList_get(KcList *list, int index, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + + void *res = NULL; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + res = entry->value; + if (size) + { + *size = entry->size; + } + } + else + { + errno = EINVAL; + } + } + return res; +} + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element と org_size が共にNULL でない場合、置き換え前の要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。また、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *remove_entry = KcLinkedList_search(info, index); + if ((remove_entry != NULL) && (remove_entry != info->tail)) + { + KcLinkedListEntry *new_entry = KcLinkedList_new_entry(element, size); + if (new_entry) + { + if ((org_element != NULL) && (org_size != NULL)) + { + size_t copy_size = (remove_entry->size < *org_size) ? remove_entry->size : *org_size; + memcpy(org_element, remove_entry->value, copy_size); + *org_size = remove_entry->size; + } + // 削除する位置に新たなエントリを追加 + new_entry->next = remove_entry->next; + new_entry->prev = remove_entry->prev; + remove_entry->prev->next = new_entry; + remove_entry->next->prev = new_entry; + free(remove_entry); + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->head->next; entry != info->tail; entry = entry->next) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->tail->prev; entry != info->head; entry = entry->prev) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Iterator +// + +/** + * LinkedListe の Iterator 管理用構造体 + */ +typedef struct KcLinkedListIteratorEntry_ +{ + KcLinkedListInfo *info; //!< ArrayList情報 + KcLinkedListEntry *current; //!< 現在のエントリ +} KcLinkedListIteratorEntry; + +/** + * 次の要素があるか否かを返します。 + * + * @param ite Iterator + * @return true/false (次の要素が存在する/存在しない) + */ +static bool KcLinkedListIterator_hasNext(KcIterator *ite) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + return (entry->current != entry->info->tail); +} + +/** + * 次の要素を取得します。 + * size が指定されている場合、次の要素が size に格納されます。 + * 次の要素がない場合、NULL を返します。 + * + * @param ite Iterator + * @param size 要素のサイズ格納用 + * @return 次の要素 + */ +static const void *KcLinkedListIterator_next(KcIterator *ite, size_t *size) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + if (size != NULL) + { + *size = entry->current->size; + } + entry->current = entry->current->next; + return (entry->current->prev->value); +} + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +static KcIterator *KcLinkedList_iterator(KcList *list, int index) +{ + size_t ite_size = sizeof(KcIterator) + sizeof(KcLinkedListIteratorEntry); + KcIterator *ite = (KcIterator *)malloc(ite_size); + if (ite) + { + ite->hasNext = KcLinkedListIterator_hasNext; + ite->next = KcLinkedListIterator_next; + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)(((KcIterator *)ite) + 1); + ite->_info = entry; + + entry->info = list->_info; + entry->current = KcLinkedList_search(entry->info, index); + } + return ite; +} + +/** + * リスト情報をクリアします。 + * + * @param list 対象リスト + */ +static void KcLinkedList_cleanup_info(KcList *list) +{ + list->clear(list); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +/** + * [内部関数] リストより指定されたインデックスのエントリを取得します。 + * インデックスが -1 または、現在の size 位置の場合、 tail を返します。 + * インデックス位置が不正な場合、エラーコード EINVAL がセットされ NULL を返します。 + * + * @param info リスト情報 + * @param index インデックス + * @return エントリ + */ +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index) +{ + + KcLinkedListEntry *entry = NULL; + kc_lock_guard(&(info->mutex)) + { + if (index > (int)info->size) + { // インデックス不正のためエラーを設定 + errno = EINVAL; + } + else if ((index == -1) || (index == (int)info->size)) + { // インデックス -1 or 末尾のため、tail を返す。 + entry = info->tail; + } + else + { + int half_size = info->size / 2; + if (index < half_size) + { // 前半のindexのため、前方より検索 + entry = info->head; + for (int idx = 0; idx <= index; idx++) + { + entry = entry->next; + } + } + else + { // 後半のindexのため、後方より検索 + // head 0 1 2 3 tail + entry = info->tail; + for (int idx = info->size; idx > index; idx--) + { + entry = entry->prev; + } + } + } + } + return entry; +} + +/** + * 新たなエントリを生成します。 + * 生成に失敗した場合、errno に ENOMEM がセットされ、NULL が返されます。 + * + * @param value 値 + * @param size サイズ + * @return 生成したエントリ + */ +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size) +{ + size_t entry_size = size + sizeof(KcLinkedListEntry); + KcLinkedListEntry *entry = (KcLinkedListEntry *)malloc(entry_size); + if (entry != NULL) + { + entry->value = (entry + 1); + entry->size = size; + memcpy(entry->value, value, size); + } + else + { // メモリ確保失敗 + errno = ENOMEM; + } + return entry; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For Quick Sort +// + +/** + * 指定されたエントリを入れ替えます。 + * 同じエントリが渡された場合は何もしません。 + * + * @param entry1 入れ替えるエントリ1 + * @param entry2 入れ替えるエントリ2 + */ +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2) +{ + if (entry1 != entry2) + { + KcLinkedListEntry *temp = entry1->next; + entry1->next = entry2->next; + entry2->next = temp; + temp = entry1->prev; + entry1->prev = entry2->prev; + entry2->prev = temp; + } +} + +/** + * Quick Sort パーティション処理 + * + * @param info LinkedList 情報 + * @param low Low + * @param high High + * @param comparator 比較関数 + * @param args 比較関数に渡されるデータ + */ +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListEntry *pivot = high; + KcLinkedListEntry *i = low->prev; + + for (KcLinkedListEntry *j = low; j != high; j = j->next) + { + int ret = comparator( + pivot->value, pivot->size, j->value, j->size, args); + if (ret >= 0) + { // j <= pivot + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, j); + } + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, high); + } + return i; +} + +/** + * Quick Sort 再帰処理 + */ +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + if ((high != info->tail) && (low != high) && (low != high->next)) + { + KcLinkedListEntry *p = KcLinkedList_sort_partition(info, low, high, comparator, args); + KcLinkedList_sort_recur(info, low, p->prev, comparator, args); + KcLinkedList_sort_recur(info, p->next, high, comparator, args); + } +} diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 3b807c0..5cc4186 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -24,7 +24,9 @@ // プロトタイプ宣言 // --- KcMemory -static void KcMemory_dump_leak(void); +// 確保中のメモリダンプは、 KcMemory_dump にて実施可能であり、 +// メモリリーク情報をダンプする KcMemory_dump_leak は明示的に公開はしない。 +void KcMemory_dump_leak(void); static bool KcMemory_print(const char *msg); // --- KcMemoryManager @@ -47,10 +49,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); static void *KcMemoryManager_reallocate_managed_ptr(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); static void KcMemoryManager_deallocate(void *ptr, KcMemoryMark expected_mark); //////////////////////////////////////////////////////////////////////////////// @@ -80,8 +78,6 @@ ._allocate = KcMemoryManager_allocate, ._reallocate = KcMemoryManager_reallocate, ._reallocate_managed_ptr = KcMemoryManager_reallocate_managed_ptr, - ._reallocate_invalid_ptr = KcMemoryManager_reallocate_invalid_ptr, - ._reallocate_unmanaged_ptr = KcMemoryManager_reallocate_unmanaged_ptr, ._deallocate = KcMemoryManager_deallocate, // --- 変数 --- @@ -116,7 +112,12 @@ void KcMemory_start(bool detail) { kc_memory_manager->_init(); - atexit(KcMemory_dump_leak); + static bool KcMemory_start = false; + if (!KcMemory_start) + { // atexit への登録は一度だけとする。 + atexit(KcMemory_dump_leak); + KcMemory_start = true; + } if (detail) { KcMemory_listener.allocate = KcMemoryListener_dump_allocate; @@ -142,7 +143,7 @@ /** * メモリリークしているメモリ情報をダンプします。 */ -static void KcMemory_dump_leak(void) +void KcMemory_dump_leak(void) { if (kc_memory_manager->_head._next != &(kc_memory_manager->_tail)) { @@ -316,7 +317,7 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 @@ -519,23 +520,9 @@ void *data_ptr = NULL; KcMemoryEntry *entry = (KcMemoryEntry *)ptr; entry--; - switch (entry->mark) + if (entry->mark == KC_MEMORY_ALLOCATED) { - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate - data_ptr = kc_memory_manager->_allocate(0, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED: // 管理されたメモリの realloc data_ptr = kc_memory_manager->_reallocate_managed_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - default: // 管理外メモリの realloc - data_ptr = kc_memory_manager->_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); - break; } return data_ptr; } @@ -568,7 +555,7 @@ KcMemoryEntry *new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); if (new_entry != NULL) { // メモリ確保成功 - kc_memory_manager->_listener.free(entry); + kc_memory_manager->_listener.free(new_entry); kc_memory_manager->_add(new_entry); kc_memory_manager->_listener.allocate(new_entry); data_ptr = new_entry->data; @@ -585,76 +572,6 @@ } // ------------------------------------- -// _reallocate_invalid_ptr -// ------------------------------------- -/** - * [内部利用関数] - * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - UNUSED_VARIABLE(ptr); - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate (invalid pointer)\n"); - errno = EINVAL; - return NULL; -} - -// ------------------------------------- -// _reallocate_unmanaged_ptr -// ------------------------------------- -/** - * [内部利用関数] - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - // | - // +----------+-------------------+ - // | 元の領域 | 追加分 + 管理領域 | - // +----------+-------------------+ - // ↓ - // ↓ memmove で 元の領域 + 追加分を、 - // ↓ 管理領域分を確保した先にコピーする - // ↓ - // +----------+----------+--------+ - // | 管理領域 | 元の領域 | 追加分 | - // +----------+----------+--------+ - void *data_ptr = NULL; - KcMemoryEntry *new_entry = KcMemoryEntry_new(ptr, 0, size, mark, file, func, line); - if (new_entry != NULL) - { // メモリ確保成功 - // memmove で 元の領域 + 追加分 をコピーして、メモリエントリとして追加する。 - memmove((new_entry + 1), new_entry, size); - kc_memory_manager->_add(new_entry); - kc_memory_manager->_listener.allocate(new_entry); - data_ptr = new_entry->data; - } - else - { // メモリ確保失敗 => エラー通知する。 - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate\n"); - } - return data_ptr; -} - -// ------------------------------------- // _deallocate // ------------------------------------- /** @@ -680,33 +597,64 @@ KcMemoryEntry_delete(entry); } else - { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 - switch (entry->mark) - { - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - case KC_MEMORY_ALLOCATED: // malloc 等で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use free)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete[])\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - default: // 管理外メモリ - free(ptr); - break; - } + { // 期待通りでない場合、管理外メモリのため stdlib.h の free にて解放する。 + raw_free(ptr); } } + +/** + * stdlib.h の malloc。 + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_malloc(size_t size) +{ + return malloc(size); +} + +/** + * stdlib.h の aligned_alloc。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} + +/** + * stdlib.h の calloc。 + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ +void *raw_calloc(size_t nmemb, size_t size) +{ + return calloc(nmemb, size); +} + +/** + * stdlib.h の realloc。 + * + * @param ptr メモリを再確保するポインタ + * @param size 確保しなおすメモリサイズ + * @return メモリへのポインタ + */ +void *raw_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +/** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ +void raw_free(void *ptr) +{ + free(ptr); +} diff --git a/modules/src/kc_memory_entry.c b/modules/src/kc_memory_entry.c index b855508..85f3fc2 100644 --- a/modules/src/kc_memory_entry.c +++ b/modules/src/kc_memory_entry.c @@ -5,9 +5,14 @@ */ #include +#include #include #include "kc_memory_entry_inner.h" +#ifdef KC_MEMORY_ENABLED +#undef KC_MEMORY_ENABLED +#endif + //////////////////////////////////////////////////////////////////////////////// // // 定数定義 @@ -16,6 +21,18 @@ /** パディング */ #define KC_MEMORY_PADDING (sizeof(void *) * 2) +//////////////////////////////////////////////////////////////////////////////// +// +// 変数定義 +// + +// For UNITTEST +#ifdef UNITTEST +bool (*_UT_KcMemory_can_alloc)( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) = NULL; +#endif // UNITTEST + /** * KcMemoryEntry を構築します。 * entry が NULL の場合、新規に KeMemoryEntry を構築します。 @@ -34,15 +51,22 @@ KcMemoryEntry *KcMemoryEntry_new(KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line) { +#ifdef UNITTEST + if (_UT_KcMemory_can_alloc && + !_UT_KcMemory_can_alloc(entry, alignment, size, mark, file, func, line)) + { + return NULL; + } +#endif KcMemoryEntry *new_entry; if ((entry == NULL) && (alignment > 0)) { // アライメント指定でメモリを確保する。 - new_entry = (KcMemoryEntry *)aligned_alloc(alignment, + new_entry = (KcMemoryEntry *)raw_aligned_alloc(alignment, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } else { - new_entry = (KcMemoryEntry *)realloc(entry, + new_entry = (KcMemoryEntry *)raw_realloc(entry, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } @@ -59,7 +83,7 @@ { entry->mark = KC_MEMORY_DELETED; entry->size = 0; - free(entry); + raw_free(entry); } /** diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index dcf722c..5bb141d 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -25,7 +25,7 @@ /** * テスト管理情報 */ -typedef struct +typedef struct KcUtInfo_ { int test_counter; //!< 実施テスト数 int ok_counter; //!< OK 件数 diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index fd0beab..99a171f 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,7 @@ * * @param size 要素のサイズ * @param cap リストの初期容量 - * @param file ファイル - * @param func 関数 - * @param line 行番号 + * @return ArrayList */ KcList *KcList_new_ArrayList(size_t size, int cap) { @@ -174,15 +173,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; bool is_contains = false; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { is_contains = true; break; @@ -213,12 +209,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; + // insert_index は常に 0 以上 int insert_index = (index < 0) ? info->size : index; bool is_success = true; kc_lock_guard(&(info->mutex)) { - is_success = ((0 <= insert_index) && (insert_index <= info->size)); - is_success = is_success && KcArrayList_increase_capacity(info); + is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info); element_type *info_data = (element_type *)info->data; if (is_success) { @@ -257,7 +253,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -323,7 +319,6 @@ * @param list 対象リスト * @param comparator リスト要素を比較するために使用される comparator * @param args comparator の第5引数に渡すオブジェクト - * @return true/false (ソート成功/ソート失敗) */ static void KcArrayList_sort(KcList *list, int (*comparator)(const void *element1, size_t size1, @@ -397,6 +392,10 @@ *size = info->element_size; } } + else + { + errno = EINVAL; + } } return res; } @@ -426,7 +425,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -465,15 +464,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; @@ -502,15 +498,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = (info->size - 1); idx >= 0; idx--) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c new file mode 100644 index 0000000..9e746a1 --- /dev/null +++ b/modules/src/kc_list_linked.c @@ -0,0 +1,721 @@ +/** + * @file kc_list_linked.c + * @brief Linked リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * KcLinkedList Entry 情報 + */ +typedef struct KcLinkedListEntry_ +{ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcLinkedListEntry_ *next; //!< 次のエントリ + struct KcLinkedListEntry_ *prev; //!< 前のエントリ +} KcLinkedListEntry; + +/** + * KcLinkedList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t size; //!< エントリ数 + KcLinkedListEntry *head; //!< 先頭エントリ + KcLinkedListEntry *tail; //!< 末尾エントリ +} KcLinkedListInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcLinkedList_size(KcList *list); +static bool KcLinkedList_is_empty(KcList *list); +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size); +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size); +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size); +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_clear(KcList *list); +static void *KcLinkedList_get(KcList *list, int index, size_t *size); +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size); +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size); +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size); +static KcIterator *KcLinkedList_iterator(KcList *list, int index); +static void KcLinkedList_cleanup_info(KcList *list); + +// [内部関数用] +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index); +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size); + +// [内部関数用] For Quick Sort +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2); +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList *KcList_new_LinkedList(void) +{ + // KcLinkedList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | next --------+ + // | prev --------|--+ + // +--------------+ | | + // | |<-+ | + // | |<----+ + // +--------------+ + KcList *list = (KcList *)malloc( + sizeof(KcList) + sizeof(KcLinkedListInfo) + (sizeof(KcLinkedListEntry) * 2)); + if (list != NULL) + { + list->size = KcLinkedList_size; + list->is_empty = KcLinkedList_is_empty; + list->contains = KcLinkedList_contains; + list->add = KcLinkedList_add; + list->remove = KcLinkedList_remove; + list->sort = KcLinkedList_sort; + list->clear = KcLinkedList_clear; + list->get = KcLinkedList_get; + list->set = KcLinkedList_set; + list->index_of = KcLinkedList_index_of; + list->last_index_of = KcLinkedList_last_index_of; + list->iterator = KcLinkedList_iterator; + list->cleanup_info = KcLinkedList_cleanup_info; + list->_info = (list + 1); + + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->head = (KcLinkedListEntry *)(info + 1); + info->tail = info->head + 1; + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } + return list; +} + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcLinkedList_size(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcLinkedList_is_empty(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = info->head->next; + while (entry != info->tail) + { + if ((entry->size == size) && (memcmp(entry->value, element, size) == 0)) + { + is_contains = true; + break; + } + entry = entry->next; + } + } + return is_contains; +} + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * index に負値を指定した場合、末尾に要素を追加します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *insert_pos = KcLinkedList_search(info, index); + if (insert_pos != NULL) + { // エントリ作成 + KcLinkedListEntry *entry = KcLinkedList_new_entry(element, size); + if (entry != NULL) + { // リストに追加 (insert_pos の一つ前に追加) + info->size++; + entry->next = insert_pos; + entry->prev = insert_pos->prev; + insert_pos->prev->next = entry; + insert_pos->prev = entry; + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element と size が共に NULL でない場合、削除された要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のバッファサイズを指定します。また、削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + if ((element != NULL) && (size != NULL)) + { + size_t copy_size = (entry->size < *size) ? entry->size : *size; + memcpy(element, entry->value, copy_size); + *size = entry->size; + } + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + free(entry); + info->size--; + is_success = true; + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedList_sort_recur( + info, + info->head->next, + info->tail->prev, + comparator, args); + } +} + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcLinkedList_clear(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry; + entry = info->head->next; + while (entry != info->tail) + { + entry = entry->next; + free(entry->prev); + } + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } +} + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +static void *KcLinkedList_get(KcList *list, int index, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + + void *res = NULL; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + res = entry->value; + if (size) + { + *size = entry->size; + } + } + else + { + errno = EINVAL; + } + } + return res; +} + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element と org_size が共にNULL でない場合、置き換え前の要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。また、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *remove_entry = KcLinkedList_search(info, index); + if ((remove_entry != NULL) && (remove_entry != info->tail)) + { + KcLinkedListEntry *new_entry = KcLinkedList_new_entry(element, size); + if (new_entry) + { + if ((org_element != NULL) && (org_size != NULL)) + { + size_t copy_size = (remove_entry->size < *org_size) ? remove_entry->size : *org_size; + memcpy(org_element, remove_entry->value, copy_size); + *org_size = remove_entry->size; + } + // 削除する位置に新たなエントリを追加 + new_entry->next = remove_entry->next; + new_entry->prev = remove_entry->prev; + remove_entry->prev->next = new_entry; + remove_entry->next->prev = new_entry; + free(remove_entry); + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->head->next; entry != info->tail; entry = entry->next) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->tail->prev; entry != info->head; entry = entry->prev) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Iterator +// + +/** + * LinkedListe の Iterator 管理用構造体 + */ +typedef struct KcLinkedListIteratorEntry_ +{ + KcLinkedListInfo *info; //!< ArrayList情報 + KcLinkedListEntry *current; //!< 現在のエントリ +} KcLinkedListIteratorEntry; + +/** + * 次の要素があるか否かを返します。 + * + * @param ite Iterator + * @return true/false (次の要素が存在する/存在しない) + */ +static bool KcLinkedListIterator_hasNext(KcIterator *ite) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + return (entry->current != entry->info->tail); +} + +/** + * 次の要素を取得します。 + * size が指定されている場合、次の要素が size に格納されます。 + * 次の要素がない場合、NULL を返します。 + * + * @param ite Iterator + * @param size 要素のサイズ格納用 + * @return 次の要素 + */ +static const void *KcLinkedListIterator_next(KcIterator *ite, size_t *size) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + if (size != NULL) + { + *size = entry->current->size; + } + entry->current = entry->current->next; + return (entry->current->prev->value); +} + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +static KcIterator *KcLinkedList_iterator(KcList *list, int index) +{ + size_t ite_size = sizeof(KcIterator) + sizeof(KcLinkedListIteratorEntry); + KcIterator *ite = (KcIterator *)malloc(ite_size); + if (ite) + { + ite->hasNext = KcLinkedListIterator_hasNext; + ite->next = KcLinkedListIterator_next; + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)(((KcIterator *)ite) + 1); + ite->_info = entry; + + entry->info = list->_info; + entry->current = KcLinkedList_search(entry->info, index); + } + return ite; +} + +/** + * リスト情報をクリアします。 + * + * @param list 対象リスト + */ +static void KcLinkedList_cleanup_info(KcList *list) +{ + list->clear(list); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +/** + * [内部関数] リストより指定されたインデックスのエントリを取得します。 + * インデックスが -1 または、現在の size 位置の場合、 tail を返します。 + * インデックス位置が不正な場合、エラーコード EINVAL がセットされ NULL を返します。 + * + * @param info リスト情報 + * @param index インデックス + * @return エントリ + */ +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index) +{ + + KcLinkedListEntry *entry = NULL; + kc_lock_guard(&(info->mutex)) + { + if (index > (int)info->size) + { // インデックス不正のためエラーを設定 + errno = EINVAL; + } + else if ((index == -1) || (index == (int)info->size)) + { // インデックス -1 or 末尾のため、tail を返す。 + entry = info->tail; + } + else + { + int half_size = info->size / 2; + if (index < half_size) + { // 前半のindexのため、前方より検索 + entry = info->head; + for (int idx = 0; idx <= index; idx++) + { + entry = entry->next; + } + } + else + { // 後半のindexのため、後方より検索 + // head 0 1 2 3 tail + entry = info->tail; + for (int idx = info->size; idx > index; idx--) + { + entry = entry->prev; + } + } + } + } + return entry; +} + +/** + * 新たなエントリを生成します。 + * 生成に失敗した場合、errno に ENOMEM がセットされ、NULL が返されます。 + * + * @param value 値 + * @param size サイズ + * @return 生成したエントリ + */ +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size) +{ + size_t entry_size = size + sizeof(KcLinkedListEntry); + KcLinkedListEntry *entry = (KcLinkedListEntry *)malloc(entry_size); + if (entry != NULL) + { + entry->value = (entry + 1); + entry->size = size; + memcpy(entry->value, value, size); + } + else + { // メモリ確保失敗 + errno = ENOMEM; + } + return entry; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For Quick Sort +// + +/** + * 指定されたエントリを入れ替えます。 + * 同じエントリが渡された場合は何もしません。 + * + * @param entry1 入れ替えるエントリ1 + * @param entry2 入れ替えるエントリ2 + */ +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2) +{ + if (entry1 != entry2) + { + KcLinkedListEntry *temp = entry1->next; + entry1->next = entry2->next; + entry2->next = temp; + temp = entry1->prev; + entry1->prev = entry2->prev; + entry2->prev = temp; + } +} + +/** + * Quick Sort パーティション処理 + * + * @param info LinkedList 情報 + * @param low Low + * @param high High + * @param comparator 比較関数 + * @param args 比較関数に渡されるデータ + */ +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListEntry *pivot = high; + KcLinkedListEntry *i = low->prev; + + for (KcLinkedListEntry *j = low; j != high; j = j->next) + { + int ret = comparator( + pivot->value, pivot->size, j->value, j->size, args); + if (ret >= 0) + { // j <= pivot + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, j); + } + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, high); + } + return i; +} + +/** + * Quick Sort 再帰処理 + */ +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + if ((high != info->tail) && (low != high) && (low != high->next)) + { + KcLinkedListEntry *p = KcLinkedList_sort_partition(info, low, high, comparator, args); + KcLinkedList_sort_recur(info, low, p->prev, comparator, args); + KcLinkedList_sort_recur(info, p->next, high, comparator, args); + } +} diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 3b807c0..5cc4186 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -24,7 +24,9 @@ // プロトタイプ宣言 // --- KcMemory -static void KcMemory_dump_leak(void); +// 確保中のメモリダンプは、 KcMemory_dump にて実施可能であり、 +// メモリリーク情報をダンプする KcMemory_dump_leak は明示的に公開はしない。 +void KcMemory_dump_leak(void); static bool KcMemory_print(const char *msg); // --- KcMemoryManager @@ -47,10 +49,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); static void *KcMemoryManager_reallocate_managed_ptr(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); static void KcMemoryManager_deallocate(void *ptr, KcMemoryMark expected_mark); //////////////////////////////////////////////////////////////////////////////// @@ -80,8 +78,6 @@ ._allocate = KcMemoryManager_allocate, ._reallocate = KcMemoryManager_reallocate, ._reallocate_managed_ptr = KcMemoryManager_reallocate_managed_ptr, - ._reallocate_invalid_ptr = KcMemoryManager_reallocate_invalid_ptr, - ._reallocate_unmanaged_ptr = KcMemoryManager_reallocate_unmanaged_ptr, ._deallocate = KcMemoryManager_deallocate, // --- 変数 --- @@ -116,7 +112,12 @@ void KcMemory_start(bool detail) { kc_memory_manager->_init(); - atexit(KcMemory_dump_leak); + static bool KcMemory_start = false; + if (!KcMemory_start) + { // atexit への登録は一度だけとする。 + atexit(KcMemory_dump_leak); + KcMemory_start = true; + } if (detail) { KcMemory_listener.allocate = KcMemoryListener_dump_allocate; @@ -142,7 +143,7 @@ /** * メモリリークしているメモリ情報をダンプします。 */ -static void KcMemory_dump_leak(void) +void KcMemory_dump_leak(void) { if (kc_memory_manager->_head._next != &(kc_memory_manager->_tail)) { @@ -316,7 +317,7 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 @@ -519,23 +520,9 @@ void *data_ptr = NULL; KcMemoryEntry *entry = (KcMemoryEntry *)ptr; entry--; - switch (entry->mark) + if (entry->mark == KC_MEMORY_ALLOCATED) { - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate - data_ptr = kc_memory_manager->_allocate(0, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED: // 管理されたメモリの realloc data_ptr = kc_memory_manager->_reallocate_managed_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - default: // 管理外メモリの realloc - data_ptr = kc_memory_manager->_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); - break; } return data_ptr; } @@ -568,7 +555,7 @@ KcMemoryEntry *new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); if (new_entry != NULL) { // メモリ確保成功 - kc_memory_manager->_listener.free(entry); + kc_memory_manager->_listener.free(new_entry); kc_memory_manager->_add(new_entry); kc_memory_manager->_listener.allocate(new_entry); data_ptr = new_entry->data; @@ -585,76 +572,6 @@ } // ------------------------------------- -// _reallocate_invalid_ptr -// ------------------------------------- -/** - * [内部利用関数] - * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - UNUSED_VARIABLE(ptr); - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate (invalid pointer)\n"); - errno = EINVAL; - return NULL; -} - -// ------------------------------------- -// _reallocate_unmanaged_ptr -// ------------------------------------- -/** - * [内部利用関数] - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - // | - // +----------+-------------------+ - // | 元の領域 | 追加分 + 管理領域 | - // +----------+-------------------+ - // ↓ - // ↓ memmove で 元の領域 + 追加分を、 - // ↓ 管理領域分を確保した先にコピーする - // ↓ - // +----------+----------+--------+ - // | 管理領域 | 元の領域 | 追加分 | - // +----------+----------+--------+ - void *data_ptr = NULL; - KcMemoryEntry *new_entry = KcMemoryEntry_new(ptr, 0, size, mark, file, func, line); - if (new_entry != NULL) - { // メモリ確保成功 - // memmove で 元の領域 + 追加分 をコピーして、メモリエントリとして追加する。 - memmove((new_entry + 1), new_entry, size); - kc_memory_manager->_add(new_entry); - kc_memory_manager->_listener.allocate(new_entry); - data_ptr = new_entry->data; - } - else - { // メモリ確保失敗 => エラー通知する。 - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate\n"); - } - return data_ptr; -} - -// ------------------------------------- // _deallocate // ------------------------------------- /** @@ -680,33 +597,64 @@ KcMemoryEntry_delete(entry); } else - { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 - switch (entry->mark) - { - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - case KC_MEMORY_ALLOCATED: // malloc 等で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use free)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete[])\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - default: // 管理外メモリ - free(ptr); - break; - } + { // 期待通りでない場合、管理外メモリのため stdlib.h の free にて解放する。 + raw_free(ptr); } } + +/** + * stdlib.h の malloc。 + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_malloc(size_t size) +{ + return malloc(size); +} + +/** + * stdlib.h の aligned_alloc。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} + +/** + * stdlib.h の calloc。 + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ +void *raw_calloc(size_t nmemb, size_t size) +{ + return calloc(nmemb, size); +} + +/** + * stdlib.h の realloc。 + * + * @param ptr メモリを再確保するポインタ + * @param size 確保しなおすメモリサイズ + * @return メモリへのポインタ + */ +void *raw_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +/** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ +void raw_free(void *ptr) +{ + free(ptr); +} diff --git a/modules/src/kc_memory_entry.c b/modules/src/kc_memory_entry.c index b855508..85f3fc2 100644 --- a/modules/src/kc_memory_entry.c +++ b/modules/src/kc_memory_entry.c @@ -5,9 +5,14 @@ */ #include +#include #include #include "kc_memory_entry_inner.h" +#ifdef KC_MEMORY_ENABLED +#undef KC_MEMORY_ENABLED +#endif + //////////////////////////////////////////////////////////////////////////////// // // 定数定義 @@ -16,6 +21,18 @@ /** パディング */ #define KC_MEMORY_PADDING (sizeof(void *) * 2) +//////////////////////////////////////////////////////////////////////////////// +// +// 変数定義 +// + +// For UNITTEST +#ifdef UNITTEST +bool (*_UT_KcMemory_can_alloc)( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) = NULL; +#endif // UNITTEST + /** * KcMemoryEntry を構築します。 * entry が NULL の場合、新規に KeMemoryEntry を構築します。 @@ -34,15 +51,22 @@ KcMemoryEntry *KcMemoryEntry_new(KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line) { +#ifdef UNITTEST + if (_UT_KcMemory_can_alloc && + !_UT_KcMemory_can_alloc(entry, alignment, size, mark, file, func, line)) + { + return NULL; + } +#endif KcMemoryEntry *new_entry; if ((entry == NULL) && (alignment > 0)) { // アライメント指定でメモリを確保する。 - new_entry = (KcMemoryEntry *)aligned_alloc(alignment, + new_entry = (KcMemoryEntry *)raw_aligned_alloc(alignment, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } else { - new_entry = (KcMemoryEntry *)realloc(entry, + new_entry = (KcMemoryEntry *)raw_realloc(entry, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } @@ -59,7 +83,7 @@ { entry->mark = KC_MEMORY_DELETED; entry->size = 0; - free(entry); + raw_free(entry); } /** diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index dcf722c..5bb141d 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -25,7 +25,7 @@ /** * テスト管理情報 */ -typedef struct +typedef struct KcUtInfo_ { int test_counter; //!< 実施テスト数 int ok_counter; //!< OK 件数 diff --git a/modules/test/src/test_assert.c b/modules/test/src/test_assert.c new file mode 100644 index 0000000..8d523f1 --- /dev/null +++ b/modules/test/src/test_assert.c @@ -0,0 +1,235 @@ +#include +#include + +#include +#include +#include + +// プロトタイプ宣言 +static void test_assert_handler(const char *msg); +static void test_assert_setup(void); +static void test_assert_teardown(void); +static void test_assert_off(void); +static void test_assert_on(void); + +static void test_assert_long(void); +static void test_assert_double(void); +static void test_assert_string(void); +static void test_assert_binary(void); +static void test_assert_null(void); +static void test_assert_true_false(void); + +/** + * assert 単体テストスイート + */ +void suite_assert(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_SETUP, "### SETUP (assert) ###", test_assert_setup); + ut->add(ut, UT_TESTCASE, "assert long", test_assert_long); + ut->add(ut, UT_TESTCASE, "assert double", test_assert_double); + ut->add(ut, UT_TESTCASE, "assert string", test_assert_string); + ut->add(ut, UT_TESTCASE, "assert binary", test_assert_binary); + ut->add(ut, UT_TESTCASE, "assert null", test_assert_null); + ut->add(ut, UT_TESTCASE, "assert true/false", test_assert_true_false); + ut->add(ut, UT_TEARDOWN, "### TEARDOWN (assert) ###", test_assert_teardown); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For assert_handler +// + +/** + * assert テスト用ハンドラが実行されたか否かを表します。 + */ +static bool test_assert_handler_pass = false; + +/** + * ut.c にて使われているハンドラバックアップ用。 + */ +static void (*test_assert_handler_backup)(const char *msg); + +/** + * assert テスト用のハンドラ。 + */ +static void test_assert_handler(const char *msg) +{ + UNUSED_VARIABLE(msg); + test_assert_handler_pass = true; +} + +/** + * assert テストのため、assert_handler を設定します。 + * ※assert にて失敗のテスト実施の際、ut 用のハンドラを使うとテストNGとなるため。 + */ +static void test_assert_setup(void) +{ + test_assert_handler_backup = assert_handler; +} + +/** + * assert テストのため、assert_handler を元にもどします。 + */ +static void test_assert_teardown(void) +{ + assert_handler = test_assert_handler_backup; +} + +/** + * 単体テストのエラー検出を無効にします。 + */ +static void test_assert_off(void) +{ + assert_handler = test_assert_handler; +} + +/** + * 単体テストのエラー検出を有効にします。 + */ +static void test_assert_on(void) +{ + assert_handler = test_assert_handler_backup; +} + +/** + * assert long 単体テスト。 + */ +static void test_assert_long(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(1234, 1234); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(1234, 1235); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert double 単体テスト。 + */ +static void test_assert_double(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 12.34, 0.1); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 12.35, 0.1); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 13.35, 0.1); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert string 単体テスト。 + */ +static void test_assert_string(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals("ABC", "ABC"); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals("ABC", "DEF"); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert binary 単体テスト。 + */ +static void test_assert_binary(void) +{ + test_assert_handler_pass = false; + char v1[] = {'A', 0, 'B', ' ', 'C', 0x81}; + char v2[] = {'A', 0, 'B', ' ', 'C', 0x81}; + char v3[] = {'A', 0, 'B', 0x19, 'C', 0x81}; + test_assert_off(); + assert_equals(v1, v2, 6); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(v1, v3, 6); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert null 単体テスト。 + */ +static void test_assert_null(void) +{ + const char *ptr = "ABC"; + const char *ptr_null = NULL; + + // not null + test_assert_handler_pass = false; + test_assert_off(); + assert_not_null(ptr); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + // null + test_assert_handler_pass = false; + test_assert_off(); + assert_null(ptr_null); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + // not null error + test_assert_handler_pass = false; + test_assert_off(); + assert_not_null(ptr_null); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + // null error + test_assert_handler_pass = false; + test_assert_off(); + assert_null(ptr); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert true/false 単体テスト。 + */ +static void test_assert_true_false(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_true(false); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_false(true); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_fail(); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} \ No newline at end of file diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index fd0beab..99a171f 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,7 @@ * * @param size 要素のサイズ * @param cap リストの初期容量 - * @param file ファイル - * @param func 関数 - * @param line 行番号 + * @return ArrayList */ KcList *KcList_new_ArrayList(size_t size, int cap) { @@ -174,15 +173,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; bool is_contains = false; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { is_contains = true; break; @@ -213,12 +209,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; + // insert_index は常に 0 以上 int insert_index = (index < 0) ? info->size : index; bool is_success = true; kc_lock_guard(&(info->mutex)) { - is_success = ((0 <= insert_index) && (insert_index <= info->size)); - is_success = is_success && KcArrayList_increase_capacity(info); + is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info); element_type *info_data = (element_type *)info->data; if (is_success) { @@ -257,7 +253,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -323,7 +319,6 @@ * @param list 対象リスト * @param comparator リスト要素を比較するために使用される comparator * @param args comparator の第5引数に渡すオブジェクト - * @return true/false (ソート成功/ソート失敗) */ static void KcArrayList_sort(KcList *list, int (*comparator)(const void *element1, size_t size1, @@ -397,6 +392,10 @@ *size = info->element_size; } } + else + { + errno = EINVAL; + } } return res; } @@ -426,7 +425,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -465,15 +464,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; @@ -502,15 +498,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = (info->size - 1); idx >= 0; idx--) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c new file mode 100644 index 0000000..9e746a1 --- /dev/null +++ b/modules/src/kc_list_linked.c @@ -0,0 +1,721 @@ +/** + * @file kc_list_linked.c + * @brief Linked リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * KcLinkedList Entry 情報 + */ +typedef struct KcLinkedListEntry_ +{ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcLinkedListEntry_ *next; //!< 次のエントリ + struct KcLinkedListEntry_ *prev; //!< 前のエントリ +} KcLinkedListEntry; + +/** + * KcLinkedList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t size; //!< エントリ数 + KcLinkedListEntry *head; //!< 先頭エントリ + KcLinkedListEntry *tail; //!< 末尾エントリ +} KcLinkedListInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcLinkedList_size(KcList *list); +static bool KcLinkedList_is_empty(KcList *list); +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size); +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size); +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size); +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_clear(KcList *list); +static void *KcLinkedList_get(KcList *list, int index, size_t *size); +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size); +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size); +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size); +static KcIterator *KcLinkedList_iterator(KcList *list, int index); +static void KcLinkedList_cleanup_info(KcList *list); + +// [内部関数用] +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index); +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size); + +// [内部関数用] For Quick Sort +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2); +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList *KcList_new_LinkedList(void) +{ + // KcLinkedList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | next --------+ + // | prev --------|--+ + // +--------------+ | | + // | |<-+ | + // | |<----+ + // +--------------+ + KcList *list = (KcList *)malloc( + sizeof(KcList) + sizeof(KcLinkedListInfo) + (sizeof(KcLinkedListEntry) * 2)); + if (list != NULL) + { + list->size = KcLinkedList_size; + list->is_empty = KcLinkedList_is_empty; + list->contains = KcLinkedList_contains; + list->add = KcLinkedList_add; + list->remove = KcLinkedList_remove; + list->sort = KcLinkedList_sort; + list->clear = KcLinkedList_clear; + list->get = KcLinkedList_get; + list->set = KcLinkedList_set; + list->index_of = KcLinkedList_index_of; + list->last_index_of = KcLinkedList_last_index_of; + list->iterator = KcLinkedList_iterator; + list->cleanup_info = KcLinkedList_cleanup_info; + list->_info = (list + 1); + + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->head = (KcLinkedListEntry *)(info + 1); + info->tail = info->head + 1; + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } + return list; +} + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcLinkedList_size(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcLinkedList_is_empty(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = info->head->next; + while (entry != info->tail) + { + if ((entry->size == size) && (memcmp(entry->value, element, size) == 0)) + { + is_contains = true; + break; + } + entry = entry->next; + } + } + return is_contains; +} + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * index に負値を指定した場合、末尾に要素を追加します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *insert_pos = KcLinkedList_search(info, index); + if (insert_pos != NULL) + { // エントリ作成 + KcLinkedListEntry *entry = KcLinkedList_new_entry(element, size); + if (entry != NULL) + { // リストに追加 (insert_pos の一つ前に追加) + info->size++; + entry->next = insert_pos; + entry->prev = insert_pos->prev; + insert_pos->prev->next = entry; + insert_pos->prev = entry; + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element と size が共に NULL でない場合、削除された要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のバッファサイズを指定します。また、削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + if ((element != NULL) && (size != NULL)) + { + size_t copy_size = (entry->size < *size) ? entry->size : *size; + memcpy(element, entry->value, copy_size); + *size = entry->size; + } + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + free(entry); + info->size--; + is_success = true; + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedList_sort_recur( + info, + info->head->next, + info->tail->prev, + comparator, args); + } +} + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcLinkedList_clear(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry; + entry = info->head->next; + while (entry != info->tail) + { + entry = entry->next; + free(entry->prev); + } + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } +} + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +static void *KcLinkedList_get(KcList *list, int index, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + + void *res = NULL; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + res = entry->value; + if (size) + { + *size = entry->size; + } + } + else + { + errno = EINVAL; + } + } + return res; +} + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element と org_size が共にNULL でない場合、置き換え前の要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。また、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *remove_entry = KcLinkedList_search(info, index); + if ((remove_entry != NULL) && (remove_entry != info->tail)) + { + KcLinkedListEntry *new_entry = KcLinkedList_new_entry(element, size); + if (new_entry) + { + if ((org_element != NULL) && (org_size != NULL)) + { + size_t copy_size = (remove_entry->size < *org_size) ? remove_entry->size : *org_size; + memcpy(org_element, remove_entry->value, copy_size); + *org_size = remove_entry->size; + } + // 削除する位置に新たなエントリを追加 + new_entry->next = remove_entry->next; + new_entry->prev = remove_entry->prev; + remove_entry->prev->next = new_entry; + remove_entry->next->prev = new_entry; + free(remove_entry); + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->head->next; entry != info->tail; entry = entry->next) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->tail->prev; entry != info->head; entry = entry->prev) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Iterator +// + +/** + * LinkedListe の Iterator 管理用構造体 + */ +typedef struct KcLinkedListIteratorEntry_ +{ + KcLinkedListInfo *info; //!< ArrayList情報 + KcLinkedListEntry *current; //!< 現在のエントリ +} KcLinkedListIteratorEntry; + +/** + * 次の要素があるか否かを返します。 + * + * @param ite Iterator + * @return true/false (次の要素が存在する/存在しない) + */ +static bool KcLinkedListIterator_hasNext(KcIterator *ite) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + return (entry->current != entry->info->tail); +} + +/** + * 次の要素を取得します。 + * size が指定されている場合、次の要素が size に格納されます。 + * 次の要素がない場合、NULL を返します。 + * + * @param ite Iterator + * @param size 要素のサイズ格納用 + * @return 次の要素 + */ +static const void *KcLinkedListIterator_next(KcIterator *ite, size_t *size) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + if (size != NULL) + { + *size = entry->current->size; + } + entry->current = entry->current->next; + return (entry->current->prev->value); +} + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +static KcIterator *KcLinkedList_iterator(KcList *list, int index) +{ + size_t ite_size = sizeof(KcIterator) + sizeof(KcLinkedListIteratorEntry); + KcIterator *ite = (KcIterator *)malloc(ite_size); + if (ite) + { + ite->hasNext = KcLinkedListIterator_hasNext; + ite->next = KcLinkedListIterator_next; + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)(((KcIterator *)ite) + 1); + ite->_info = entry; + + entry->info = list->_info; + entry->current = KcLinkedList_search(entry->info, index); + } + return ite; +} + +/** + * リスト情報をクリアします。 + * + * @param list 対象リスト + */ +static void KcLinkedList_cleanup_info(KcList *list) +{ + list->clear(list); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +/** + * [内部関数] リストより指定されたインデックスのエントリを取得します。 + * インデックスが -1 または、現在の size 位置の場合、 tail を返します。 + * インデックス位置が不正な場合、エラーコード EINVAL がセットされ NULL を返します。 + * + * @param info リスト情報 + * @param index インデックス + * @return エントリ + */ +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index) +{ + + KcLinkedListEntry *entry = NULL; + kc_lock_guard(&(info->mutex)) + { + if (index > (int)info->size) + { // インデックス不正のためエラーを設定 + errno = EINVAL; + } + else if ((index == -1) || (index == (int)info->size)) + { // インデックス -1 or 末尾のため、tail を返す。 + entry = info->tail; + } + else + { + int half_size = info->size / 2; + if (index < half_size) + { // 前半のindexのため、前方より検索 + entry = info->head; + for (int idx = 0; idx <= index; idx++) + { + entry = entry->next; + } + } + else + { // 後半のindexのため、後方より検索 + // head 0 1 2 3 tail + entry = info->tail; + for (int idx = info->size; idx > index; idx--) + { + entry = entry->prev; + } + } + } + } + return entry; +} + +/** + * 新たなエントリを生成します。 + * 生成に失敗した場合、errno に ENOMEM がセットされ、NULL が返されます。 + * + * @param value 値 + * @param size サイズ + * @return 生成したエントリ + */ +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size) +{ + size_t entry_size = size + sizeof(KcLinkedListEntry); + KcLinkedListEntry *entry = (KcLinkedListEntry *)malloc(entry_size); + if (entry != NULL) + { + entry->value = (entry + 1); + entry->size = size; + memcpy(entry->value, value, size); + } + else + { // メモリ確保失敗 + errno = ENOMEM; + } + return entry; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For Quick Sort +// + +/** + * 指定されたエントリを入れ替えます。 + * 同じエントリが渡された場合は何もしません。 + * + * @param entry1 入れ替えるエントリ1 + * @param entry2 入れ替えるエントリ2 + */ +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2) +{ + if (entry1 != entry2) + { + KcLinkedListEntry *temp = entry1->next; + entry1->next = entry2->next; + entry2->next = temp; + temp = entry1->prev; + entry1->prev = entry2->prev; + entry2->prev = temp; + } +} + +/** + * Quick Sort パーティション処理 + * + * @param info LinkedList 情報 + * @param low Low + * @param high High + * @param comparator 比較関数 + * @param args 比較関数に渡されるデータ + */ +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListEntry *pivot = high; + KcLinkedListEntry *i = low->prev; + + for (KcLinkedListEntry *j = low; j != high; j = j->next) + { + int ret = comparator( + pivot->value, pivot->size, j->value, j->size, args); + if (ret >= 0) + { // j <= pivot + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, j); + } + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, high); + } + return i; +} + +/** + * Quick Sort 再帰処理 + */ +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + if ((high != info->tail) && (low != high) && (low != high->next)) + { + KcLinkedListEntry *p = KcLinkedList_sort_partition(info, low, high, comparator, args); + KcLinkedList_sort_recur(info, low, p->prev, comparator, args); + KcLinkedList_sort_recur(info, p->next, high, comparator, args); + } +} diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 3b807c0..5cc4186 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -24,7 +24,9 @@ // プロトタイプ宣言 // --- KcMemory -static void KcMemory_dump_leak(void); +// 確保中のメモリダンプは、 KcMemory_dump にて実施可能であり、 +// メモリリーク情報をダンプする KcMemory_dump_leak は明示的に公開はしない。 +void KcMemory_dump_leak(void); static bool KcMemory_print(const char *msg); // --- KcMemoryManager @@ -47,10 +49,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); static void *KcMemoryManager_reallocate_managed_ptr(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); static void KcMemoryManager_deallocate(void *ptr, KcMemoryMark expected_mark); //////////////////////////////////////////////////////////////////////////////// @@ -80,8 +78,6 @@ ._allocate = KcMemoryManager_allocate, ._reallocate = KcMemoryManager_reallocate, ._reallocate_managed_ptr = KcMemoryManager_reallocate_managed_ptr, - ._reallocate_invalid_ptr = KcMemoryManager_reallocate_invalid_ptr, - ._reallocate_unmanaged_ptr = KcMemoryManager_reallocate_unmanaged_ptr, ._deallocate = KcMemoryManager_deallocate, // --- 変数 --- @@ -116,7 +112,12 @@ void KcMemory_start(bool detail) { kc_memory_manager->_init(); - atexit(KcMemory_dump_leak); + static bool KcMemory_start = false; + if (!KcMemory_start) + { // atexit への登録は一度だけとする。 + atexit(KcMemory_dump_leak); + KcMemory_start = true; + } if (detail) { KcMemory_listener.allocate = KcMemoryListener_dump_allocate; @@ -142,7 +143,7 @@ /** * メモリリークしているメモリ情報をダンプします。 */ -static void KcMemory_dump_leak(void) +void KcMemory_dump_leak(void) { if (kc_memory_manager->_head._next != &(kc_memory_manager->_tail)) { @@ -316,7 +317,7 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 @@ -519,23 +520,9 @@ void *data_ptr = NULL; KcMemoryEntry *entry = (KcMemoryEntry *)ptr; entry--; - switch (entry->mark) + if (entry->mark == KC_MEMORY_ALLOCATED) { - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate - data_ptr = kc_memory_manager->_allocate(0, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED: // 管理されたメモリの realloc data_ptr = kc_memory_manager->_reallocate_managed_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - default: // 管理外メモリの realloc - data_ptr = kc_memory_manager->_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); - break; } return data_ptr; } @@ -568,7 +555,7 @@ KcMemoryEntry *new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); if (new_entry != NULL) { // メモリ確保成功 - kc_memory_manager->_listener.free(entry); + kc_memory_manager->_listener.free(new_entry); kc_memory_manager->_add(new_entry); kc_memory_manager->_listener.allocate(new_entry); data_ptr = new_entry->data; @@ -585,76 +572,6 @@ } // ------------------------------------- -// _reallocate_invalid_ptr -// ------------------------------------- -/** - * [内部利用関数] - * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - UNUSED_VARIABLE(ptr); - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate (invalid pointer)\n"); - errno = EINVAL; - return NULL; -} - -// ------------------------------------- -// _reallocate_unmanaged_ptr -// ------------------------------------- -/** - * [内部利用関数] - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - // | - // +----------+-------------------+ - // | 元の領域 | 追加分 + 管理領域 | - // +----------+-------------------+ - // ↓ - // ↓ memmove で 元の領域 + 追加分を、 - // ↓ 管理領域分を確保した先にコピーする - // ↓ - // +----------+----------+--------+ - // | 管理領域 | 元の領域 | 追加分 | - // +----------+----------+--------+ - void *data_ptr = NULL; - KcMemoryEntry *new_entry = KcMemoryEntry_new(ptr, 0, size, mark, file, func, line); - if (new_entry != NULL) - { // メモリ確保成功 - // memmove で 元の領域 + 追加分 をコピーして、メモリエントリとして追加する。 - memmove((new_entry + 1), new_entry, size); - kc_memory_manager->_add(new_entry); - kc_memory_manager->_listener.allocate(new_entry); - data_ptr = new_entry->data; - } - else - { // メモリ確保失敗 => エラー通知する。 - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate\n"); - } - return data_ptr; -} - -// ------------------------------------- // _deallocate // ------------------------------------- /** @@ -680,33 +597,64 @@ KcMemoryEntry_delete(entry); } else - { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 - switch (entry->mark) - { - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - case KC_MEMORY_ALLOCATED: // malloc 等で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use free)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete[])\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - default: // 管理外メモリ - free(ptr); - break; - } + { // 期待通りでない場合、管理外メモリのため stdlib.h の free にて解放する。 + raw_free(ptr); } } + +/** + * stdlib.h の malloc。 + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_malloc(size_t size) +{ + return malloc(size); +} + +/** + * stdlib.h の aligned_alloc。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} + +/** + * stdlib.h の calloc。 + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ +void *raw_calloc(size_t nmemb, size_t size) +{ + return calloc(nmemb, size); +} + +/** + * stdlib.h の realloc。 + * + * @param ptr メモリを再確保するポインタ + * @param size 確保しなおすメモリサイズ + * @return メモリへのポインタ + */ +void *raw_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +/** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ +void raw_free(void *ptr) +{ + free(ptr); +} diff --git a/modules/src/kc_memory_entry.c b/modules/src/kc_memory_entry.c index b855508..85f3fc2 100644 --- a/modules/src/kc_memory_entry.c +++ b/modules/src/kc_memory_entry.c @@ -5,9 +5,14 @@ */ #include +#include #include #include "kc_memory_entry_inner.h" +#ifdef KC_MEMORY_ENABLED +#undef KC_MEMORY_ENABLED +#endif + //////////////////////////////////////////////////////////////////////////////// // // 定数定義 @@ -16,6 +21,18 @@ /** パディング */ #define KC_MEMORY_PADDING (sizeof(void *) * 2) +//////////////////////////////////////////////////////////////////////////////// +// +// 変数定義 +// + +// For UNITTEST +#ifdef UNITTEST +bool (*_UT_KcMemory_can_alloc)( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) = NULL; +#endif // UNITTEST + /** * KcMemoryEntry を構築します。 * entry が NULL の場合、新規に KeMemoryEntry を構築します。 @@ -34,15 +51,22 @@ KcMemoryEntry *KcMemoryEntry_new(KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line) { +#ifdef UNITTEST + if (_UT_KcMemory_can_alloc && + !_UT_KcMemory_can_alloc(entry, alignment, size, mark, file, func, line)) + { + return NULL; + } +#endif KcMemoryEntry *new_entry; if ((entry == NULL) && (alignment > 0)) { // アライメント指定でメモリを確保する。 - new_entry = (KcMemoryEntry *)aligned_alloc(alignment, + new_entry = (KcMemoryEntry *)raw_aligned_alloc(alignment, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } else { - new_entry = (KcMemoryEntry *)realloc(entry, + new_entry = (KcMemoryEntry *)raw_realloc(entry, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } @@ -59,7 +83,7 @@ { entry->mark = KC_MEMORY_DELETED; entry->size = 0; - free(entry); + raw_free(entry); } /** diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index dcf722c..5bb141d 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -25,7 +25,7 @@ /** * テスト管理情報 */ -typedef struct +typedef struct KcUtInfo_ { int test_counter; //!< 実施テスト数 int ok_counter; //!< OK 件数 diff --git a/modules/test/src/test_assert.c b/modules/test/src/test_assert.c new file mode 100644 index 0000000..8d523f1 --- /dev/null +++ b/modules/test/src/test_assert.c @@ -0,0 +1,235 @@ +#include +#include + +#include +#include +#include + +// プロトタイプ宣言 +static void test_assert_handler(const char *msg); +static void test_assert_setup(void); +static void test_assert_teardown(void); +static void test_assert_off(void); +static void test_assert_on(void); + +static void test_assert_long(void); +static void test_assert_double(void); +static void test_assert_string(void); +static void test_assert_binary(void); +static void test_assert_null(void); +static void test_assert_true_false(void); + +/** + * assert 単体テストスイート + */ +void suite_assert(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_SETUP, "### SETUP (assert) ###", test_assert_setup); + ut->add(ut, UT_TESTCASE, "assert long", test_assert_long); + ut->add(ut, UT_TESTCASE, "assert double", test_assert_double); + ut->add(ut, UT_TESTCASE, "assert string", test_assert_string); + ut->add(ut, UT_TESTCASE, "assert binary", test_assert_binary); + ut->add(ut, UT_TESTCASE, "assert null", test_assert_null); + ut->add(ut, UT_TESTCASE, "assert true/false", test_assert_true_false); + ut->add(ut, UT_TEARDOWN, "### TEARDOWN (assert) ###", test_assert_teardown); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For assert_handler +// + +/** + * assert テスト用ハンドラが実行されたか否かを表します。 + */ +static bool test_assert_handler_pass = false; + +/** + * ut.c にて使われているハンドラバックアップ用。 + */ +static void (*test_assert_handler_backup)(const char *msg); + +/** + * assert テスト用のハンドラ。 + */ +static void test_assert_handler(const char *msg) +{ + UNUSED_VARIABLE(msg); + test_assert_handler_pass = true; +} + +/** + * assert テストのため、assert_handler を設定します。 + * ※assert にて失敗のテスト実施の際、ut 用のハンドラを使うとテストNGとなるため。 + */ +static void test_assert_setup(void) +{ + test_assert_handler_backup = assert_handler; +} + +/** + * assert テストのため、assert_handler を元にもどします。 + */ +static void test_assert_teardown(void) +{ + assert_handler = test_assert_handler_backup; +} + +/** + * 単体テストのエラー検出を無効にします。 + */ +static void test_assert_off(void) +{ + assert_handler = test_assert_handler; +} + +/** + * 単体テストのエラー検出を有効にします。 + */ +static void test_assert_on(void) +{ + assert_handler = test_assert_handler_backup; +} + +/** + * assert long 単体テスト。 + */ +static void test_assert_long(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(1234, 1234); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(1234, 1235); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert double 単体テスト。 + */ +static void test_assert_double(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 12.34, 0.1); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 12.35, 0.1); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 13.35, 0.1); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert string 単体テスト。 + */ +static void test_assert_string(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals("ABC", "ABC"); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals("ABC", "DEF"); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert binary 単体テスト。 + */ +static void test_assert_binary(void) +{ + test_assert_handler_pass = false; + char v1[] = {'A', 0, 'B', ' ', 'C', 0x81}; + char v2[] = {'A', 0, 'B', ' ', 'C', 0x81}; + char v3[] = {'A', 0, 'B', 0x19, 'C', 0x81}; + test_assert_off(); + assert_equals(v1, v2, 6); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(v1, v3, 6); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert null 単体テスト。 + */ +static void test_assert_null(void) +{ + const char *ptr = "ABC"; + const char *ptr_null = NULL; + + // not null + test_assert_handler_pass = false; + test_assert_off(); + assert_not_null(ptr); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + // null + test_assert_handler_pass = false; + test_assert_off(); + assert_null(ptr_null); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + // not null error + test_assert_handler_pass = false; + test_assert_off(); + assert_not_null(ptr_null); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + // null error + test_assert_handler_pass = false; + test_assert_off(); + assert_null(ptr); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert true/false 単体テスト。 + */ +static void test_assert_true_false(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_true(false); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_false(true); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_fail(); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} \ No newline at end of file diff --git a/modules/test/src/test_list_array.c b/modules/test/src/test_list_array.c index 5fa1256..1dec74e 100644 --- a/modules/test/src/test_list_array.c +++ b/modules/test/src/test_list_array.c @@ -17,7 +17,7 @@ static void test_list_array_malloc_error(void); /** - * memory_mark 単体テストスイート + * ArrayList 単体テストスイート */ void suite_list_array(void) { @@ -57,7 +57,8 @@ * @process 追加(index 不正) * @process 値取得(サイズ取得あり) * @process 値取得(サイズ取得なし) - * @process 値取得 (index 不正) + * @process 値取得 (index 不正[負の値を指定]) + * @process 値取得 (index 不正[サイズ以上を指定]) */ static void test_list_array_add(void) { @@ -114,7 +115,11 @@ res_val = list->get(list, 3, NULL); assert_equals(40, *res_val); - // 存在しないデータ取得 + // 値取得 (index 不正[負の値を指定]) + res_val = list->get(list, -1, NULL); + assert_null(res_val); + + // 値取得 (index 不正[サイズ以上を指定]) res_val = list->get(list, 4, NULL); assert_null(res_val); @@ -127,8 +132,10 @@ * @process 末尾削除 * @process 中間削除 * @process 先頭削除 - * @process 削除(index 不正) + * @process 削除(index 不正[負の値を指定]) + * @process 削除(index 不正[サイズ以上を指定]) * @process 残っているデータを確認 + * @process 容量調整(初期容量→1/4以下) */ static void test_list_array_remove(void) { @@ -175,7 +182,11 @@ now_size = list->size(list); assert_equals((default_size - 2), now_size); - // 削除(index 不正) + // 削除(index 不正[負の値指定]) + ret = list->remove(list, -1, NULL, NULL); + assert_false(ret); + + // 削除(index 不正[サイズ以上を指定]) ret = list->remove(list, default_size, NULL, NULL); assert_false(ret); @@ -206,10 +217,27 @@ assert_true(is_empty); KcList_delete(list); + + // 容量調整(初期容量→1/4以下) + KcList *big_list = KcList_new_ArrayList(sizeof(int), 12); + int big_val = 10; + // 追加 + bool big_ret = big_list->add(list, 0, &big_val, sizeof(int)); + assert_true(big_ret); + // 削除 (この時、容量調整発生) + big_ret = big_list->remove(list, 0, NULL, NULL); + assert_true(big_ret); + KcList_delete(big_list); } /** * ArrayList データ設定。 + * + * @process 値設定(複数設定) + * @process 値設定(元の値、サイズ取得) + * @process 値設定(元の値取得) + * @process 値設定(不正index[負の値指定]) + * @process 値設定(不正index[サイズ以上指定]) */ static void test_list_array_set(void) { @@ -246,14 +274,53 @@ res_val = list->get(list, 0, NULL); assert_equals(98, *res_val); + // 値設定 (サイズ取得) + set_value = 97; + ret = list->set(list, 0, &set_value, sizeof(int), NULL, &orig_size); + assert_true(ret); + assert_equals((int)sizeof(int), (int)orig_size); + res_val = list->get(list, 0, NULL); + assert_equals(97, *res_val); + now_size = list->size(list); assert_equals(5, now_size); + // 値設定(不正index[負の値指定]) + set_value = 96; + ret = list->set(list, -1, &set_value, sizeof(int), NULL, NULL); + assert_false(ret); + + // 値設定(不正index[サイズ以上指定]) + set_value = 95; + ret = list->set(list, 5, &set_value, sizeof(int), NULL, NULL); + assert_false(ret); + KcList_delete(list); } /** * ArrayList 検索。 + * + * @process リスト {10, 30, 50, 40, 20, 10, 30, 60, 20 } を構築する。 + * @result リストが構築されること + * + * @process is_contains でリストに含まれる値を確認する。 + * @result true が返されること。 + * + * @process index_of で各インデックス位置を取得する。 + * @result 先頭からのインデックス位置が正しく取得されること。 + * + * @process last_index_of で各インデックス位置を取得する。 + * @result 末尾からのインデックス位置が正しく取得されること。 + * + * @process リストに含まれない値を指定して、is_contains を実行する。 + * @result false が返されること。 + * + * @process リストに含まれない値を指定して、index_of を実行する。 + * @result -1 が返されること。 + * + * @process リストに含まれない値を指定して、last_index_of を実行する。 + * @result -1 が返されること。 */ static void test_list_array_search(void) { @@ -321,6 +388,15 @@ /** * ArrayList ソート。 + * + * @process リスト {10, 30, 50, 40, 20, 10, 30, 60, 20} を構築する。 + * @result リストが構築されること。 + * + * @process 昇順用コンパレータを渡し、ソートする。 + * @result リストがソートされること。 + * + * @process Iterator を取得する。 + * @result Iterator にて順次値が取得できること。 */ static void test_list_array_sort(void) { @@ -342,6 +418,7 @@ list->sort(list, test_list_array_sort_comparator, "ABCDEFG"); int sorted_vals[] = {10, 10, 20, 20, 30, 30, 40, 50, 60}; + // Iterator にて値を取得&確認 KcIterator *ite = list->iterator(list, 0); int sorted_index = 0; while (ite->hasNext(ite)) @@ -368,12 +445,71 @@ KcList_delete(list); } +#include "ut.h" +static int UT_ListArray_can_alloc_counter = 0; +static bool UT_ListArray_can_alloc( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) +{ + UNUSED_VARIABLE(entry); + UNUSED_VARIABLE(alignment); + UNUSED_VARIABLE(size); + UNUSED_VARIABLE(mark); + UNUSED_VARIABLE(file); + UNUSED_VARIABLE(func); + UNUSED_VARIABLE(line); + + UT_ListArray_can_alloc_counter--; + if (UT_ListArray_can_alloc_counter < 0) + { + return false; + } + return true; +} + /** * ArrayList メモリ確保失敗。 */ - static void test_list_array_malloc_error(void) { - KcList *list = KcList_new_ArrayList(sizeof(int), -1); + // ArrayList のメモリ確保失敗 + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + KcList *list = KcList_new_ArrayList(sizeof(int), 3); + _UT_KcMemory_can_alloc = NULL; assert_null(list); + + // ArrayList のデータ用メモリ確保失敗 + UT_ListArray_can_alloc_counter = 1; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + list = KcList_new_ArrayList(sizeof(int), 3); + _UT_KcMemory_can_alloc = NULL; + assert_null(list); + + // Array Size 拡張時のメモリ確保失敗 + list = KcList_new_ArrayList(sizeof(int), 3); + int val = 10; + list->add(list, 0, &val, sizeof(int)); + list->add(list, 1, &val, sizeof(int)); + list->add(list, 2, &val, sizeof(int)); + + bool ret; + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + // size 以上 + ret = list->add(list, 3, &val, sizeof(int)); + assert_false(ret); + // 0 指定 + ret = list->add(list, 0, &val, sizeof(int)); + assert_false(ret); + _UT_KcMemory_can_alloc = NULL; + + // Iterator 生成時のメモリ確保失敗 + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + KcIterator *ite = list->iterator(list, 0); + _UT_KcMemory_can_alloc = NULL; + assert_null(ite); + + KcList_delete(list); } \ No newline at end of file diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index fd0beab..99a171f 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,7 @@ * * @param size 要素のサイズ * @param cap リストの初期容量 - * @param file ファイル - * @param func 関数 - * @param line 行番号 + * @return ArrayList */ KcList *KcList_new_ArrayList(size_t size, int cap) { @@ -174,15 +173,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; bool is_contains = false; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { is_contains = true; break; @@ -213,12 +209,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; + // insert_index は常に 0 以上 int insert_index = (index < 0) ? info->size : index; bool is_success = true; kc_lock_guard(&(info->mutex)) { - is_success = ((0 <= insert_index) && (insert_index <= info->size)); - is_success = is_success && KcArrayList_increase_capacity(info); + is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info); element_type *info_data = (element_type *)info->data; if (is_success) { @@ -257,7 +253,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -323,7 +319,6 @@ * @param list 対象リスト * @param comparator リスト要素を比較するために使用される comparator * @param args comparator の第5引数に渡すオブジェクト - * @return true/false (ソート成功/ソート失敗) */ static void KcArrayList_sort(KcList *list, int (*comparator)(const void *element1, size_t size1, @@ -397,6 +392,10 @@ *size = info->element_size; } } + else + { + errno = EINVAL; + } } return res; } @@ -426,7 +425,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -465,15 +464,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; @@ -502,15 +498,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = (info->size - 1); idx >= 0; idx--) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c new file mode 100644 index 0000000..9e746a1 --- /dev/null +++ b/modules/src/kc_list_linked.c @@ -0,0 +1,721 @@ +/** + * @file kc_list_linked.c + * @brief Linked リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * KcLinkedList Entry 情報 + */ +typedef struct KcLinkedListEntry_ +{ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcLinkedListEntry_ *next; //!< 次のエントリ + struct KcLinkedListEntry_ *prev; //!< 前のエントリ +} KcLinkedListEntry; + +/** + * KcLinkedList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t size; //!< エントリ数 + KcLinkedListEntry *head; //!< 先頭エントリ + KcLinkedListEntry *tail; //!< 末尾エントリ +} KcLinkedListInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcLinkedList_size(KcList *list); +static bool KcLinkedList_is_empty(KcList *list); +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size); +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size); +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size); +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_clear(KcList *list); +static void *KcLinkedList_get(KcList *list, int index, size_t *size); +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size); +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size); +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size); +static KcIterator *KcLinkedList_iterator(KcList *list, int index); +static void KcLinkedList_cleanup_info(KcList *list); + +// [内部関数用] +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index); +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size); + +// [内部関数用] For Quick Sort +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2); +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList *KcList_new_LinkedList(void) +{ + // KcLinkedList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | next --------+ + // | prev --------|--+ + // +--------------+ | | + // | |<-+ | + // | |<----+ + // +--------------+ + KcList *list = (KcList *)malloc( + sizeof(KcList) + sizeof(KcLinkedListInfo) + (sizeof(KcLinkedListEntry) * 2)); + if (list != NULL) + { + list->size = KcLinkedList_size; + list->is_empty = KcLinkedList_is_empty; + list->contains = KcLinkedList_contains; + list->add = KcLinkedList_add; + list->remove = KcLinkedList_remove; + list->sort = KcLinkedList_sort; + list->clear = KcLinkedList_clear; + list->get = KcLinkedList_get; + list->set = KcLinkedList_set; + list->index_of = KcLinkedList_index_of; + list->last_index_of = KcLinkedList_last_index_of; + list->iterator = KcLinkedList_iterator; + list->cleanup_info = KcLinkedList_cleanup_info; + list->_info = (list + 1); + + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->head = (KcLinkedListEntry *)(info + 1); + info->tail = info->head + 1; + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } + return list; +} + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcLinkedList_size(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcLinkedList_is_empty(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = info->head->next; + while (entry != info->tail) + { + if ((entry->size == size) && (memcmp(entry->value, element, size) == 0)) + { + is_contains = true; + break; + } + entry = entry->next; + } + } + return is_contains; +} + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * index に負値を指定した場合、末尾に要素を追加します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *insert_pos = KcLinkedList_search(info, index); + if (insert_pos != NULL) + { // エントリ作成 + KcLinkedListEntry *entry = KcLinkedList_new_entry(element, size); + if (entry != NULL) + { // リストに追加 (insert_pos の一つ前に追加) + info->size++; + entry->next = insert_pos; + entry->prev = insert_pos->prev; + insert_pos->prev->next = entry; + insert_pos->prev = entry; + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element と size が共に NULL でない場合、削除された要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のバッファサイズを指定します。また、削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + if ((element != NULL) && (size != NULL)) + { + size_t copy_size = (entry->size < *size) ? entry->size : *size; + memcpy(element, entry->value, copy_size); + *size = entry->size; + } + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + free(entry); + info->size--; + is_success = true; + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedList_sort_recur( + info, + info->head->next, + info->tail->prev, + comparator, args); + } +} + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcLinkedList_clear(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry; + entry = info->head->next; + while (entry != info->tail) + { + entry = entry->next; + free(entry->prev); + } + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } +} + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +static void *KcLinkedList_get(KcList *list, int index, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + + void *res = NULL; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + res = entry->value; + if (size) + { + *size = entry->size; + } + } + else + { + errno = EINVAL; + } + } + return res; +} + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element と org_size が共にNULL でない場合、置き換え前の要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。また、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *remove_entry = KcLinkedList_search(info, index); + if ((remove_entry != NULL) && (remove_entry != info->tail)) + { + KcLinkedListEntry *new_entry = KcLinkedList_new_entry(element, size); + if (new_entry) + { + if ((org_element != NULL) && (org_size != NULL)) + { + size_t copy_size = (remove_entry->size < *org_size) ? remove_entry->size : *org_size; + memcpy(org_element, remove_entry->value, copy_size); + *org_size = remove_entry->size; + } + // 削除する位置に新たなエントリを追加 + new_entry->next = remove_entry->next; + new_entry->prev = remove_entry->prev; + remove_entry->prev->next = new_entry; + remove_entry->next->prev = new_entry; + free(remove_entry); + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->head->next; entry != info->tail; entry = entry->next) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->tail->prev; entry != info->head; entry = entry->prev) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Iterator +// + +/** + * LinkedListe の Iterator 管理用構造体 + */ +typedef struct KcLinkedListIteratorEntry_ +{ + KcLinkedListInfo *info; //!< ArrayList情報 + KcLinkedListEntry *current; //!< 現在のエントリ +} KcLinkedListIteratorEntry; + +/** + * 次の要素があるか否かを返します。 + * + * @param ite Iterator + * @return true/false (次の要素が存在する/存在しない) + */ +static bool KcLinkedListIterator_hasNext(KcIterator *ite) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + return (entry->current != entry->info->tail); +} + +/** + * 次の要素を取得します。 + * size が指定されている場合、次の要素が size に格納されます。 + * 次の要素がない場合、NULL を返します。 + * + * @param ite Iterator + * @param size 要素のサイズ格納用 + * @return 次の要素 + */ +static const void *KcLinkedListIterator_next(KcIterator *ite, size_t *size) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + if (size != NULL) + { + *size = entry->current->size; + } + entry->current = entry->current->next; + return (entry->current->prev->value); +} + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +static KcIterator *KcLinkedList_iterator(KcList *list, int index) +{ + size_t ite_size = sizeof(KcIterator) + sizeof(KcLinkedListIteratorEntry); + KcIterator *ite = (KcIterator *)malloc(ite_size); + if (ite) + { + ite->hasNext = KcLinkedListIterator_hasNext; + ite->next = KcLinkedListIterator_next; + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)(((KcIterator *)ite) + 1); + ite->_info = entry; + + entry->info = list->_info; + entry->current = KcLinkedList_search(entry->info, index); + } + return ite; +} + +/** + * リスト情報をクリアします。 + * + * @param list 対象リスト + */ +static void KcLinkedList_cleanup_info(KcList *list) +{ + list->clear(list); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +/** + * [内部関数] リストより指定されたインデックスのエントリを取得します。 + * インデックスが -1 または、現在の size 位置の場合、 tail を返します。 + * インデックス位置が不正な場合、エラーコード EINVAL がセットされ NULL を返します。 + * + * @param info リスト情報 + * @param index インデックス + * @return エントリ + */ +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index) +{ + + KcLinkedListEntry *entry = NULL; + kc_lock_guard(&(info->mutex)) + { + if (index > (int)info->size) + { // インデックス不正のためエラーを設定 + errno = EINVAL; + } + else if ((index == -1) || (index == (int)info->size)) + { // インデックス -1 or 末尾のため、tail を返す。 + entry = info->tail; + } + else + { + int half_size = info->size / 2; + if (index < half_size) + { // 前半のindexのため、前方より検索 + entry = info->head; + for (int idx = 0; idx <= index; idx++) + { + entry = entry->next; + } + } + else + { // 後半のindexのため、後方より検索 + // head 0 1 2 3 tail + entry = info->tail; + for (int idx = info->size; idx > index; idx--) + { + entry = entry->prev; + } + } + } + } + return entry; +} + +/** + * 新たなエントリを生成します。 + * 生成に失敗した場合、errno に ENOMEM がセットされ、NULL が返されます。 + * + * @param value 値 + * @param size サイズ + * @return 生成したエントリ + */ +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size) +{ + size_t entry_size = size + sizeof(KcLinkedListEntry); + KcLinkedListEntry *entry = (KcLinkedListEntry *)malloc(entry_size); + if (entry != NULL) + { + entry->value = (entry + 1); + entry->size = size; + memcpy(entry->value, value, size); + } + else + { // メモリ確保失敗 + errno = ENOMEM; + } + return entry; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For Quick Sort +// + +/** + * 指定されたエントリを入れ替えます。 + * 同じエントリが渡された場合は何もしません。 + * + * @param entry1 入れ替えるエントリ1 + * @param entry2 入れ替えるエントリ2 + */ +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2) +{ + if (entry1 != entry2) + { + KcLinkedListEntry *temp = entry1->next; + entry1->next = entry2->next; + entry2->next = temp; + temp = entry1->prev; + entry1->prev = entry2->prev; + entry2->prev = temp; + } +} + +/** + * Quick Sort パーティション処理 + * + * @param info LinkedList 情報 + * @param low Low + * @param high High + * @param comparator 比較関数 + * @param args 比較関数に渡されるデータ + */ +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListEntry *pivot = high; + KcLinkedListEntry *i = low->prev; + + for (KcLinkedListEntry *j = low; j != high; j = j->next) + { + int ret = comparator( + pivot->value, pivot->size, j->value, j->size, args); + if (ret >= 0) + { // j <= pivot + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, j); + } + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, high); + } + return i; +} + +/** + * Quick Sort 再帰処理 + */ +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + if ((high != info->tail) && (low != high) && (low != high->next)) + { + KcLinkedListEntry *p = KcLinkedList_sort_partition(info, low, high, comparator, args); + KcLinkedList_sort_recur(info, low, p->prev, comparator, args); + KcLinkedList_sort_recur(info, p->next, high, comparator, args); + } +} diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 3b807c0..5cc4186 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -24,7 +24,9 @@ // プロトタイプ宣言 // --- KcMemory -static void KcMemory_dump_leak(void); +// 確保中のメモリダンプは、 KcMemory_dump にて実施可能であり、 +// メモリリーク情報をダンプする KcMemory_dump_leak は明示的に公開はしない。 +void KcMemory_dump_leak(void); static bool KcMemory_print(const char *msg); // --- KcMemoryManager @@ -47,10 +49,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); static void *KcMemoryManager_reallocate_managed_ptr(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); static void KcMemoryManager_deallocate(void *ptr, KcMemoryMark expected_mark); //////////////////////////////////////////////////////////////////////////////// @@ -80,8 +78,6 @@ ._allocate = KcMemoryManager_allocate, ._reallocate = KcMemoryManager_reallocate, ._reallocate_managed_ptr = KcMemoryManager_reallocate_managed_ptr, - ._reallocate_invalid_ptr = KcMemoryManager_reallocate_invalid_ptr, - ._reallocate_unmanaged_ptr = KcMemoryManager_reallocate_unmanaged_ptr, ._deallocate = KcMemoryManager_deallocate, // --- 変数 --- @@ -116,7 +112,12 @@ void KcMemory_start(bool detail) { kc_memory_manager->_init(); - atexit(KcMemory_dump_leak); + static bool KcMemory_start = false; + if (!KcMemory_start) + { // atexit への登録は一度だけとする。 + atexit(KcMemory_dump_leak); + KcMemory_start = true; + } if (detail) { KcMemory_listener.allocate = KcMemoryListener_dump_allocate; @@ -142,7 +143,7 @@ /** * メモリリークしているメモリ情報をダンプします。 */ -static void KcMemory_dump_leak(void) +void KcMemory_dump_leak(void) { if (kc_memory_manager->_head._next != &(kc_memory_manager->_tail)) { @@ -316,7 +317,7 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 @@ -519,23 +520,9 @@ void *data_ptr = NULL; KcMemoryEntry *entry = (KcMemoryEntry *)ptr; entry--; - switch (entry->mark) + if (entry->mark == KC_MEMORY_ALLOCATED) { - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate - data_ptr = kc_memory_manager->_allocate(0, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED: // 管理されたメモリの realloc data_ptr = kc_memory_manager->_reallocate_managed_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - default: // 管理外メモリの realloc - data_ptr = kc_memory_manager->_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); - break; } return data_ptr; } @@ -568,7 +555,7 @@ KcMemoryEntry *new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); if (new_entry != NULL) { // メモリ確保成功 - kc_memory_manager->_listener.free(entry); + kc_memory_manager->_listener.free(new_entry); kc_memory_manager->_add(new_entry); kc_memory_manager->_listener.allocate(new_entry); data_ptr = new_entry->data; @@ -585,76 +572,6 @@ } // ------------------------------------- -// _reallocate_invalid_ptr -// ------------------------------------- -/** - * [内部利用関数] - * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - UNUSED_VARIABLE(ptr); - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate (invalid pointer)\n"); - errno = EINVAL; - return NULL; -} - -// ------------------------------------- -// _reallocate_unmanaged_ptr -// ------------------------------------- -/** - * [内部利用関数] - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - // | - // +----------+-------------------+ - // | 元の領域 | 追加分 + 管理領域 | - // +----------+-------------------+ - // ↓ - // ↓ memmove で 元の領域 + 追加分を、 - // ↓ 管理領域分を確保した先にコピーする - // ↓ - // +----------+----------+--------+ - // | 管理領域 | 元の領域 | 追加分 | - // +----------+----------+--------+ - void *data_ptr = NULL; - KcMemoryEntry *new_entry = KcMemoryEntry_new(ptr, 0, size, mark, file, func, line); - if (new_entry != NULL) - { // メモリ確保成功 - // memmove で 元の領域 + 追加分 をコピーして、メモリエントリとして追加する。 - memmove((new_entry + 1), new_entry, size); - kc_memory_manager->_add(new_entry); - kc_memory_manager->_listener.allocate(new_entry); - data_ptr = new_entry->data; - } - else - { // メモリ確保失敗 => エラー通知する。 - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate\n"); - } - return data_ptr; -} - -// ------------------------------------- // _deallocate // ------------------------------------- /** @@ -680,33 +597,64 @@ KcMemoryEntry_delete(entry); } else - { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 - switch (entry->mark) - { - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - case KC_MEMORY_ALLOCATED: // malloc 等で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use free)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete[])\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - default: // 管理外メモリ - free(ptr); - break; - } + { // 期待通りでない場合、管理外メモリのため stdlib.h の free にて解放する。 + raw_free(ptr); } } + +/** + * stdlib.h の malloc。 + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_malloc(size_t size) +{ + return malloc(size); +} + +/** + * stdlib.h の aligned_alloc。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} + +/** + * stdlib.h の calloc。 + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ +void *raw_calloc(size_t nmemb, size_t size) +{ + return calloc(nmemb, size); +} + +/** + * stdlib.h の realloc。 + * + * @param ptr メモリを再確保するポインタ + * @param size 確保しなおすメモリサイズ + * @return メモリへのポインタ + */ +void *raw_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +/** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ +void raw_free(void *ptr) +{ + free(ptr); +} diff --git a/modules/src/kc_memory_entry.c b/modules/src/kc_memory_entry.c index b855508..85f3fc2 100644 --- a/modules/src/kc_memory_entry.c +++ b/modules/src/kc_memory_entry.c @@ -5,9 +5,14 @@ */ #include +#include #include #include "kc_memory_entry_inner.h" +#ifdef KC_MEMORY_ENABLED +#undef KC_MEMORY_ENABLED +#endif + //////////////////////////////////////////////////////////////////////////////// // // 定数定義 @@ -16,6 +21,18 @@ /** パディング */ #define KC_MEMORY_PADDING (sizeof(void *) * 2) +//////////////////////////////////////////////////////////////////////////////// +// +// 変数定義 +// + +// For UNITTEST +#ifdef UNITTEST +bool (*_UT_KcMemory_can_alloc)( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) = NULL; +#endif // UNITTEST + /** * KcMemoryEntry を構築します。 * entry が NULL の場合、新規に KeMemoryEntry を構築します。 @@ -34,15 +51,22 @@ KcMemoryEntry *KcMemoryEntry_new(KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line) { +#ifdef UNITTEST + if (_UT_KcMemory_can_alloc && + !_UT_KcMemory_can_alloc(entry, alignment, size, mark, file, func, line)) + { + return NULL; + } +#endif KcMemoryEntry *new_entry; if ((entry == NULL) && (alignment > 0)) { // アライメント指定でメモリを確保する。 - new_entry = (KcMemoryEntry *)aligned_alloc(alignment, + new_entry = (KcMemoryEntry *)raw_aligned_alloc(alignment, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } else { - new_entry = (KcMemoryEntry *)realloc(entry, + new_entry = (KcMemoryEntry *)raw_realloc(entry, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } @@ -59,7 +83,7 @@ { entry->mark = KC_MEMORY_DELETED; entry->size = 0; - free(entry); + raw_free(entry); } /** diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index dcf722c..5bb141d 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -25,7 +25,7 @@ /** * テスト管理情報 */ -typedef struct +typedef struct KcUtInfo_ { int test_counter; //!< 実施テスト数 int ok_counter; //!< OK 件数 diff --git a/modules/test/src/test_assert.c b/modules/test/src/test_assert.c new file mode 100644 index 0000000..8d523f1 --- /dev/null +++ b/modules/test/src/test_assert.c @@ -0,0 +1,235 @@ +#include +#include + +#include +#include +#include + +// プロトタイプ宣言 +static void test_assert_handler(const char *msg); +static void test_assert_setup(void); +static void test_assert_teardown(void); +static void test_assert_off(void); +static void test_assert_on(void); + +static void test_assert_long(void); +static void test_assert_double(void); +static void test_assert_string(void); +static void test_assert_binary(void); +static void test_assert_null(void); +static void test_assert_true_false(void); + +/** + * assert 単体テストスイート + */ +void suite_assert(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_SETUP, "### SETUP (assert) ###", test_assert_setup); + ut->add(ut, UT_TESTCASE, "assert long", test_assert_long); + ut->add(ut, UT_TESTCASE, "assert double", test_assert_double); + ut->add(ut, UT_TESTCASE, "assert string", test_assert_string); + ut->add(ut, UT_TESTCASE, "assert binary", test_assert_binary); + ut->add(ut, UT_TESTCASE, "assert null", test_assert_null); + ut->add(ut, UT_TESTCASE, "assert true/false", test_assert_true_false); + ut->add(ut, UT_TEARDOWN, "### TEARDOWN (assert) ###", test_assert_teardown); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For assert_handler +// + +/** + * assert テスト用ハンドラが実行されたか否かを表します。 + */ +static bool test_assert_handler_pass = false; + +/** + * ut.c にて使われているハンドラバックアップ用。 + */ +static void (*test_assert_handler_backup)(const char *msg); + +/** + * assert テスト用のハンドラ。 + */ +static void test_assert_handler(const char *msg) +{ + UNUSED_VARIABLE(msg); + test_assert_handler_pass = true; +} + +/** + * assert テストのため、assert_handler を設定します。 + * ※assert にて失敗のテスト実施の際、ut 用のハンドラを使うとテストNGとなるため。 + */ +static void test_assert_setup(void) +{ + test_assert_handler_backup = assert_handler; +} + +/** + * assert テストのため、assert_handler を元にもどします。 + */ +static void test_assert_teardown(void) +{ + assert_handler = test_assert_handler_backup; +} + +/** + * 単体テストのエラー検出を無効にします。 + */ +static void test_assert_off(void) +{ + assert_handler = test_assert_handler; +} + +/** + * 単体テストのエラー検出を有効にします。 + */ +static void test_assert_on(void) +{ + assert_handler = test_assert_handler_backup; +} + +/** + * assert long 単体テスト。 + */ +static void test_assert_long(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(1234, 1234); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(1234, 1235); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert double 単体テスト。 + */ +static void test_assert_double(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 12.34, 0.1); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 12.35, 0.1); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 13.35, 0.1); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert string 単体テスト。 + */ +static void test_assert_string(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals("ABC", "ABC"); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals("ABC", "DEF"); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert binary 単体テスト。 + */ +static void test_assert_binary(void) +{ + test_assert_handler_pass = false; + char v1[] = {'A', 0, 'B', ' ', 'C', 0x81}; + char v2[] = {'A', 0, 'B', ' ', 'C', 0x81}; + char v3[] = {'A', 0, 'B', 0x19, 'C', 0x81}; + test_assert_off(); + assert_equals(v1, v2, 6); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(v1, v3, 6); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert null 単体テスト。 + */ +static void test_assert_null(void) +{ + const char *ptr = "ABC"; + const char *ptr_null = NULL; + + // not null + test_assert_handler_pass = false; + test_assert_off(); + assert_not_null(ptr); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + // null + test_assert_handler_pass = false; + test_assert_off(); + assert_null(ptr_null); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + // not null error + test_assert_handler_pass = false; + test_assert_off(); + assert_not_null(ptr_null); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + // null error + test_assert_handler_pass = false; + test_assert_off(); + assert_null(ptr); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert true/false 単体テスト。 + */ +static void test_assert_true_false(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_true(false); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_false(true); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_fail(); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} \ No newline at end of file diff --git a/modules/test/src/test_list_array.c b/modules/test/src/test_list_array.c index 5fa1256..1dec74e 100644 --- a/modules/test/src/test_list_array.c +++ b/modules/test/src/test_list_array.c @@ -17,7 +17,7 @@ static void test_list_array_malloc_error(void); /** - * memory_mark 単体テストスイート + * ArrayList 単体テストスイート */ void suite_list_array(void) { @@ -57,7 +57,8 @@ * @process 追加(index 不正) * @process 値取得(サイズ取得あり) * @process 値取得(サイズ取得なし) - * @process 値取得 (index 不正) + * @process 値取得 (index 不正[負の値を指定]) + * @process 値取得 (index 不正[サイズ以上を指定]) */ static void test_list_array_add(void) { @@ -114,7 +115,11 @@ res_val = list->get(list, 3, NULL); assert_equals(40, *res_val); - // 存在しないデータ取得 + // 値取得 (index 不正[負の値を指定]) + res_val = list->get(list, -1, NULL); + assert_null(res_val); + + // 値取得 (index 不正[サイズ以上を指定]) res_val = list->get(list, 4, NULL); assert_null(res_val); @@ -127,8 +132,10 @@ * @process 末尾削除 * @process 中間削除 * @process 先頭削除 - * @process 削除(index 不正) + * @process 削除(index 不正[負の値を指定]) + * @process 削除(index 不正[サイズ以上を指定]) * @process 残っているデータを確認 + * @process 容量調整(初期容量→1/4以下) */ static void test_list_array_remove(void) { @@ -175,7 +182,11 @@ now_size = list->size(list); assert_equals((default_size - 2), now_size); - // 削除(index 不正) + // 削除(index 不正[負の値指定]) + ret = list->remove(list, -1, NULL, NULL); + assert_false(ret); + + // 削除(index 不正[サイズ以上を指定]) ret = list->remove(list, default_size, NULL, NULL); assert_false(ret); @@ -206,10 +217,27 @@ assert_true(is_empty); KcList_delete(list); + + // 容量調整(初期容量→1/4以下) + KcList *big_list = KcList_new_ArrayList(sizeof(int), 12); + int big_val = 10; + // 追加 + bool big_ret = big_list->add(list, 0, &big_val, sizeof(int)); + assert_true(big_ret); + // 削除 (この時、容量調整発生) + big_ret = big_list->remove(list, 0, NULL, NULL); + assert_true(big_ret); + KcList_delete(big_list); } /** * ArrayList データ設定。 + * + * @process 値設定(複数設定) + * @process 値設定(元の値、サイズ取得) + * @process 値設定(元の値取得) + * @process 値設定(不正index[負の値指定]) + * @process 値設定(不正index[サイズ以上指定]) */ static void test_list_array_set(void) { @@ -246,14 +274,53 @@ res_val = list->get(list, 0, NULL); assert_equals(98, *res_val); + // 値設定 (サイズ取得) + set_value = 97; + ret = list->set(list, 0, &set_value, sizeof(int), NULL, &orig_size); + assert_true(ret); + assert_equals((int)sizeof(int), (int)orig_size); + res_val = list->get(list, 0, NULL); + assert_equals(97, *res_val); + now_size = list->size(list); assert_equals(5, now_size); + // 値設定(不正index[負の値指定]) + set_value = 96; + ret = list->set(list, -1, &set_value, sizeof(int), NULL, NULL); + assert_false(ret); + + // 値設定(不正index[サイズ以上指定]) + set_value = 95; + ret = list->set(list, 5, &set_value, sizeof(int), NULL, NULL); + assert_false(ret); + KcList_delete(list); } /** * ArrayList 検索。 + * + * @process リスト {10, 30, 50, 40, 20, 10, 30, 60, 20 } を構築する。 + * @result リストが構築されること + * + * @process is_contains でリストに含まれる値を確認する。 + * @result true が返されること。 + * + * @process index_of で各インデックス位置を取得する。 + * @result 先頭からのインデックス位置が正しく取得されること。 + * + * @process last_index_of で各インデックス位置を取得する。 + * @result 末尾からのインデックス位置が正しく取得されること。 + * + * @process リストに含まれない値を指定して、is_contains を実行する。 + * @result false が返されること。 + * + * @process リストに含まれない値を指定して、index_of を実行する。 + * @result -1 が返されること。 + * + * @process リストに含まれない値を指定して、last_index_of を実行する。 + * @result -1 が返されること。 */ static void test_list_array_search(void) { @@ -321,6 +388,15 @@ /** * ArrayList ソート。 + * + * @process リスト {10, 30, 50, 40, 20, 10, 30, 60, 20} を構築する。 + * @result リストが構築されること。 + * + * @process 昇順用コンパレータを渡し、ソートする。 + * @result リストがソートされること。 + * + * @process Iterator を取得する。 + * @result Iterator にて順次値が取得できること。 */ static void test_list_array_sort(void) { @@ -342,6 +418,7 @@ list->sort(list, test_list_array_sort_comparator, "ABCDEFG"); int sorted_vals[] = {10, 10, 20, 20, 30, 30, 40, 50, 60}; + // Iterator にて値を取得&確認 KcIterator *ite = list->iterator(list, 0); int sorted_index = 0; while (ite->hasNext(ite)) @@ -368,12 +445,71 @@ KcList_delete(list); } +#include "ut.h" +static int UT_ListArray_can_alloc_counter = 0; +static bool UT_ListArray_can_alloc( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) +{ + UNUSED_VARIABLE(entry); + UNUSED_VARIABLE(alignment); + UNUSED_VARIABLE(size); + UNUSED_VARIABLE(mark); + UNUSED_VARIABLE(file); + UNUSED_VARIABLE(func); + UNUSED_VARIABLE(line); + + UT_ListArray_can_alloc_counter--; + if (UT_ListArray_can_alloc_counter < 0) + { + return false; + } + return true; +} + /** * ArrayList メモリ確保失敗。 */ - static void test_list_array_malloc_error(void) { - KcList *list = KcList_new_ArrayList(sizeof(int), -1); + // ArrayList のメモリ確保失敗 + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + KcList *list = KcList_new_ArrayList(sizeof(int), 3); + _UT_KcMemory_can_alloc = NULL; assert_null(list); + + // ArrayList のデータ用メモリ確保失敗 + UT_ListArray_can_alloc_counter = 1; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + list = KcList_new_ArrayList(sizeof(int), 3); + _UT_KcMemory_can_alloc = NULL; + assert_null(list); + + // Array Size 拡張時のメモリ確保失敗 + list = KcList_new_ArrayList(sizeof(int), 3); + int val = 10; + list->add(list, 0, &val, sizeof(int)); + list->add(list, 1, &val, sizeof(int)); + list->add(list, 2, &val, sizeof(int)); + + bool ret; + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + // size 以上 + ret = list->add(list, 3, &val, sizeof(int)); + assert_false(ret); + // 0 指定 + ret = list->add(list, 0, &val, sizeof(int)); + assert_false(ret); + _UT_KcMemory_can_alloc = NULL; + + // Iterator 生成時のメモリ確保失敗 + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + KcIterator *ite = list->iterator(list, 0); + _UT_KcMemory_can_alloc = NULL; + assert_null(ite); + + KcList_delete(list); } \ No newline at end of file diff --git a/modules/test/src/test_memory.c b/modules/test/src/test_memory.c index ddf6d0d..b8b953e 100644 --- a/modules/test/src/test_memory.c +++ b/modules/test/src/test_memory.c @@ -1,33 +1,314 @@ #include +#include + #include #include #include +#include -static void test_1(void) -{ - assert_equals("a", "a"); -} +#include "ut.h" -static void test_2(void) -{ - assert_equals(1, 2); -} -static void setup(void) -{ - // NOP -} -static void test_3(void) -{ - assert_true(1 == 1); -} +// プロトタイプ宣言 +static void test_memory_start(void); +static void test_memory_dump(void); +static void test_memory_set_listener(void); +static void test_memory_entries(void); +static void test_memory_freeif(void); +static void test_memory_aligned(void); +static void test_memory_calloc(void); +static void test_memory_realloc(void); +static void test_memory_raw_xxxxx(void); -void test_memory(void) +/** + * memory_mark 単体テストスイート + */ +void suite_memory(void) { KcUt *ut = KcUt_get_instance(); - ut->add(ut, UT_SETUP, "SETUP", setup); - ut->add(ut, UT_TESTCASE, "テスト1", test_1); - ut->add(ut, UT_SETUP, "SETUP", setup); - ut->add(ut, UT_TESTCASE, "テスト2", test_2); - ut->add(ut, UT_SETUP, "SETUP", setup); - ut->add(ut, UT_TESTCASE, "テスト3", test_3); + ut->add(ut, UT_TESTCASE, "memory start", test_memory_start); + ut->add(ut, UT_TESTCASE, "memory dump", test_memory_dump); + ut->add(ut, UT_TESTCASE, "memory set_listener", test_memory_set_listener); + ut->add(ut, UT_TESTCASE, "memory entries", test_memory_entries); + ut->add(ut, UT_TESTCASE, "memory freeif", test_memory_freeif); + ut->add(ut, UT_TESTCASE, "memory aligned", test_memory_aligned); + ut->add(ut, UT_TESTCASE, "memory calloc", test_memory_calloc); + ut->add(ut, UT_TESTCASE, "memory realloc", test_memory_realloc); + ut->add(ut, UT_TESTCASE, "memory raw_xxxxx", test_memory_raw_xxxxx); +} + +/** + * メモリ管理開始。 + * + * @process メモリ管理開始(情報詳細出力あり) + * @process メモリ管理開始(情報詳細出力無し) + */ +static void test_memory_start(void) +{ + // NOP + // ut.c にて、下記実施 + // KcMemory_start(false); + // KcMemory_start(true); +} + +extern void KcMemory_dump_leak(void); +static bool test_memory_dump_handler(const char *data) +{ + printf("%s", data); + return true; +} + +/** + * 確保中のメモリ情報出力。 + * + * @process dump を実行する。 + * @result 確保中のメモリ情報が出力されること。 + * + * @param dump_leak を実行する。 + * @result メモリリーク情報が出力されること。 + * + * @process 確保しているメモリがない状態で dump を実行する。 + * @result メモリ情報が出力されないこと。 + * + * @process 出力用ハンドラを指定してメモリをダンプする。 + * @result ハンドラに対してメモリ情報が渡されること。 + */ +static void test_memory_dump(void) +{ + int *val = (int *)malloc(sizeof(int)); + *val = 10; + + printf("---DUMP---\n"); + KcMemory_dump(); + + printf("---DUMP LEAK---\n"); + KcMemory_dump_leak(); + + free(val); + printf("---DUMP---\n"); + KcMemory_dump(); + + val = (int *)malloc(sizeof(int)); + *val = 20; + kc_memory_manager->dump( + test_memory_dump_handler, 16, true, true, 8192); + free(val); +} + +/** + * リスナ設定(NULL指定)。 + * + * @process リスナを登録する。(NULL指定) + * @result リスナが設定されないこと。 + */ +static void test_memory_set_listener(void) +{ + KcMemoryListener listener; + listener.allocate = NULL; + listener.free = NULL; + listener.error = NULL; + kc_memory_manager->set_listener(&listener); +} + +// ハンドラ実行用 +static int test_memory_entries_handler_counter = 0; +static int test_memory_entries_handler_values[10]; +static int test_memory_entries_handler_can_loop = 0; +static bool test_memory_entries_handler(const KcMemoryEntry *entry, void *info) +{ + assert_equals( + test_memory_entries_handler_values[test_memory_entries_handler_counter], + *((int *)entry->data)); + test_memory_entries_handler_counter++; + test_memory_entries_handler_can_loop--; + assert_equals("X", (const char *)info); + if (test_memory_entries_handler_can_loop <= 0) + { + return false; + } + return true; +} +/** + * メモリエントリハンドラ実行 + */ +static void test_memory_entries(void) +{ + int *val1 = (int *)malloc(sizeof(int)); + int *val2 = (int *)malloc(sizeof(int)); + int *val3 = (int *)malloc(sizeof(int)); + *val1 = 123; + *val2 = 456; + *val3 = 789; + test_memory_entries_handler_values[0] = 123; + test_memory_entries_handler_values[1] = 456; + test_memory_entries_handler_values[2] = 789; + + // 途中中断パターン + test_memory_entries_handler_counter = 0; + test_memory_entries_handler_can_loop = 2; + kc_memory_manager->entries(test_memory_entries_handler, "X"); + assert_equals(2, test_memory_entries_handler_counter); + + // 全てダンプパターン + test_memory_entries_handler_counter = 0; + test_memory_entries_handler_can_loop = 999; + kc_memory_manager->entries(test_memory_entries_handler, "X"); + assert_equals(3, test_memory_entries_handler_counter); + + free(val1); + free(val2); + free(val3); +} + +bool test_memory_freeif_handler(const KcMemoryEntry *entry, void *info) +{ + assert_equals("FREEIF", (const char *)info); + if (strcmp("test_memory_freeif", entry->func) == 0) + { + int *data = (int *)entry->data; + if (*data == 456) + { + return true; + } + } + return false; +} + +/** + * 強制メモリ破棄 + */ +static void test_memory_freeif(void) +{ + int *val1 = (int *)malloc(sizeof(int)); + int *val2 = (int *)malloc(sizeof(int)); + int *val3 = (int *)malloc(sizeof(int)); + *val1 = 123; + *val2 = 456; + *val3 = 789; + test_memory_entries_handler_values[0] = 123; + test_memory_entries_handler_values[1] = 456; + test_memory_entries_handler_values[2] = 789; + + kc_memory_manager->freeif(test_memory_freeif_handler, "FREEIF"); + free(val1); + // free(val2); + free(val3); +} + +// メモリ確保失敗動作確認用 +static int UT_Memory_can_alloc_counter = 0; +static bool UT_Memory_can_alloc( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) +{ + UNUSED_VARIABLE(entry); + UNUSED_VARIABLE(alignment); + UNUSED_VARIABLE(size); + UNUSED_VARIABLE(mark); + UNUSED_VARIABLE(file); + UNUSED_VARIABLE(func); + UNUSED_VARIABLE(line); + + UT_Memory_can_alloc_counter--; + if (UT_Memory_can_alloc_counter < 0) + { + return false; + } + return true; +} +/** + * アライメント指定メモリ確保 + * + * @process aligned_alloc によりメモリを確保する。 + * @result メモリが確保されること。 + * + * @process メモリ確保が失敗する状態で、aligned_alloc を実行する。 + * @result メモリ確保に失敗し、NULL が返されること。 + */ +static void test_memory_aligned(void) +{ + int *val = (int *)aligned_alloc(sizeof(long long), sizeof(int)); + assert_not_null(val); + *val = 123; + free(val); + + UT_Memory_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_Memory_can_alloc; + val = (int *)aligned_alloc(sizeof(long long), sizeof(int)); + assert_null(val); + _UT_KcMemory_can_alloc = NULL; +} + +/** + * calloc によるメモリ確保 + * + * @process calloc によりメモリを確保する。 + * @result メモリが確保されること。内容がすべて 0 のこと。 + * + * @process メモリ確保が失敗する状態で、calloc を実行する。 + * @result メモリ確保に失敗し、NULL が返されること。 + */ +static void test_memory_calloc(void) +{ + int *val = (int *)calloc(10, sizeof(int)); + for (int i = 0; i < 10; i++) + { + assert_equals(0, val[i]); + } + free(val); + + UT_Memory_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_Memory_can_alloc; + val = (int *)calloc(10, sizeof(int)); + assert_null(val); + _UT_KcMemory_can_alloc = NULL; +} + +/** + * realloc によるメモリ確保 + * + * @process NULL を指定して、realloc によりメモリを確保する。 + * @result メモリが確保されること。 + * + * @process メモリ確保が失敗する状態で、calloc を実行する。 + * @result メモリ確保に失敗し、NULL が返されること。 + */ +static void test_memory_realloc(void) +{ + // NULL 指定 + int *val = realloc(NULL, sizeof(int)); + assert_not_null(val); + *val = 123; + free(val); + + // 管理外メモリによる realloc + int *unmng_ptr = raw_malloc(sizeof(int) * 2); + unmng_ptr[0] = 123; + unmng_ptr[1] = 456; + assert_equals(123, unmng_ptr[0]); + assert_equals(456, unmng_ptr[1]); + + int *mng_ptr = (int *)realloc(unmng_ptr, sizeof(int) * 3); + assert_null(mng_ptr); + free(unmng_ptr); +} + +/** + * raw_xxxxx 関数動作確認。 + */ +static void test_memory_raw_xxxxx(void) +{ + int *ptr = (int *)raw_malloc(sizeof(int)); + assert_not_null(ptr); + raw_free(ptr); + + ptr = raw_aligned_alloc(sizeof(int), sizeof(int)); + assert_not_null(ptr); + raw_free(ptr); + + ptr = raw_calloc(10, sizeof(int)); + assert_not_null(ptr); + + ptr = raw_realloc(ptr, 20); + assert_not_null(ptr); + raw_free(ptr); } diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index fd0beab..99a171f 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,7 @@ * * @param size 要素のサイズ * @param cap リストの初期容量 - * @param file ファイル - * @param func 関数 - * @param line 行番号 + * @return ArrayList */ KcList *KcList_new_ArrayList(size_t size, int cap) { @@ -174,15 +173,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; bool is_contains = false; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { is_contains = true; break; @@ -213,12 +209,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; + // insert_index は常に 0 以上 int insert_index = (index < 0) ? info->size : index; bool is_success = true; kc_lock_guard(&(info->mutex)) { - is_success = ((0 <= insert_index) && (insert_index <= info->size)); - is_success = is_success && KcArrayList_increase_capacity(info); + is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info); element_type *info_data = (element_type *)info->data; if (is_success) { @@ -257,7 +253,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -323,7 +319,6 @@ * @param list 対象リスト * @param comparator リスト要素を比較するために使用される comparator * @param args comparator の第5引数に渡すオブジェクト - * @return true/false (ソート成功/ソート失敗) */ static void KcArrayList_sort(KcList *list, int (*comparator)(const void *element1, size_t size1, @@ -397,6 +392,10 @@ *size = info->element_size; } } + else + { + errno = EINVAL; + } } return res; } @@ -426,7 +425,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -465,15 +464,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; @@ -502,15 +498,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = (info->size - 1); idx >= 0; idx--) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c new file mode 100644 index 0000000..9e746a1 --- /dev/null +++ b/modules/src/kc_list_linked.c @@ -0,0 +1,721 @@ +/** + * @file kc_list_linked.c + * @brief Linked リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * KcLinkedList Entry 情報 + */ +typedef struct KcLinkedListEntry_ +{ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcLinkedListEntry_ *next; //!< 次のエントリ + struct KcLinkedListEntry_ *prev; //!< 前のエントリ +} KcLinkedListEntry; + +/** + * KcLinkedList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t size; //!< エントリ数 + KcLinkedListEntry *head; //!< 先頭エントリ + KcLinkedListEntry *tail; //!< 末尾エントリ +} KcLinkedListInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcLinkedList_size(KcList *list); +static bool KcLinkedList_is_empty(KcList *list); +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size); +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size); +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size); +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_clear(KcList *list); +static void *KcLinkedList_get(KcList *list, int index, size_t *size); +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size); +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size); +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size); +static KcIterator *KcLinkedList_iterator(KcList *list, int index); +static void KcLinkedList_cleanup_info(KcList *list); + +// [内部関数用] +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index); +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size); + +// [内部関数用] For Quick Sort +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2); +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList *KcList_new_LinkedList(void) +{ + // KcLinkedList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | next --------+ + // | prev --------|--+ + // +--------------+ | | + // | |<-+ | + // | |<----+ + // +--------------+ + KcList *list = (KcList *)malloc( + sizeof(KcList) + sizeof(KcLinkedListInfo) + (sizeof(KcLinkedListEntry) * 2)); + if (list != NULL) + { + list->size = KcLinkedList_size; + list->is_empty = KcLinkedList_is_empty; + list->contains = KcLinkedList_contains; + list->add = KcLinkedList_add; + list->remove = KcLinkedList_remove; + list->sort = KcLinkedList_sort; + list->clear = KcLinkedList_clear; + list->get = KcLinkedList_get; + list->set = KcLinkedList_set; + list->index_of = KcLinkedList_index_of; + list->last_index_of = KcLinkedList_last_index_of; + list->iterator = KcLinkedList_iterator; + list->cleanup_info = KcLinkedList_cleanup_info; + list->_info = (list + 1); + + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->head = (KcLinkedListEntry *)(info + 1); + info->tail = info->head + 1; + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } + return list; +} + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcLinkedList_size(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcLinkedList_is_empty(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = info->head->next; + while (entry != info->tail) + { + if ((entry->size == size) && (memcmp(entry->value, element, size) == 0)) + { + is_contains = true; + break; + } + entry = entry->next; + } + } + return is_contains; +} + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * index に負値を指定した場合、末尾に要素を追加します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *insert_pos = KcLinkedList_search(info, index); + if (insert_pos != NULL) + { // エントリ作成 + KcLinkedListEntry *entry = KcLinkedList_new_entry(element, size); + if (entry != NULL) + { // リストに追加 (insert_pos の一つ前に追加) + info->size++; + entry->next = insert_pos; + entry->prev = insert_pos->prev; + insert_pos->prev->next = entry; + insert_pos->prev = entry; + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element と size が共に NULL でない場合、削除された要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のバッファサイズを指定します。また、削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + if ((element != NULL) && (size != NULL)) + { + size_t copy_size = (entry->size < *size) ? entry->size : *size; + memcpy(element, entry->value, copy_size); + *size = entry->size; + } + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + free(entry); + info->size--; + is_success = true; + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedList_sort_recur( + info, + info->head->next, + info->tail->prev, + comparator, args); + } +} + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcLinkedList_clear(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry; + entry = info->head->next; + while (entry != info->tail) + { + entry = entry->next; + free(entry->prev); + } + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } +} + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +static void *KcLinkedList_get(KcList *list, int index, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + + void *res = NULL; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + res = entry->value; + if (size) + { + *size = entry->size; + } + } + else + { + errno = EINVAL; + } + } + return res; +} + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element と org_size が共にNULL でない場合、置き換え前の要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。また、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *remove_entry = KcLinkedList_search(info, index); + if ((remove_entry != NULL) && (remove_entry != info->tail)) + { + KcLinkedListEntry *new_entry = KcLinkedList_new_entry(element, size); + if (new_entry) + { + if ((org_element != NULL) && (org_size != NULL)) + { + size_t copy_size = (remove_entry->size < *org_size) ? remove_entry->size : *org_size; + memcpy(org_element, remove_entry->value, copy_size); + *org_size = remove_entry->size; + } + // 削除する位置に新たなエントリを追加 + new_entry->next = remove_entry->next; + new_entry->prev = remove_entry->prev; + remove_entry->prev->next = new_entry; + remove_entry->next->prev = new_entry; + free(remove_entry); + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->head->next; entry != info->tail; entry = entry->next) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->tail->prev; entry != info->head; entry = entry->prev) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Iterator +// + +/** + * LinkedListe の Iterator 管理用構造体 + */ +typedef struct KcLinkedListIteratorEntry_ +{ + KcLinkedListInfo *info; //!< ArrayList情報 + KcLinkedListEntry *current; //!< 現在のエントリ +} KcLinkedListIteratorEntry; + +/** + * 次の要素があるか否かを返します。 + * + * @param ite Iterator + * @return true/false (次の要素が存在する/存在しない) + */ +static bool KcLinkedListIterator_hasNext(KcIterator *ite) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + return (entry->current != entry->info->tail); +} + +/** + * 次の要素を取得します。 + * size が指定されている場合、次の要素が size に格納されます。 + * 次の要素がない場合、NULL を返します。 + * + * @param ite Iterator + * @param size 要素のサイズ格納用 + * @return 次の要素 + */ +static const void *KcLinkedListIterator_next(KcIterator *ite, size_t *size) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + if (size != NULL) + { + *size = entry->current->size; + } + entry->current = entry->current->next; + return (entry->current->prev->value); +} + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +static KcIterator *KcLinkedList_iterator(KcList *list, int index) +{ + size_t ite_size = sizeof(KcIterator) + sizeof(KcLinkedListIteratorEntry); + KcIterator *ite = (KcIterator *)malloc(ite_size); + if (ite) + { + ite->hasNext = KcLinkedListIterator_hasNext; + ite->next = KcLinkedListIterator_next; + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)(((KcIterator *)ite) + 1); + ite->_info = entry; + + entry->info = list->_info; + entry->current = KcLinkedList_search(entry->info, index); + } + return ite; +} + +/** + * リスト情報をクリアします。 + * + * @param list 対象リスト + */ +static void KcLinkedList_cleanup_info(KcList *list) +{ + list->clear(list); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +/** + * [内部関数] リストより指定されたインデックスのエントリを取得します。 + * インデックスが -1 または、現在の size 位置の場合、 tail を返します。 + * インデックス位置が不正な場合、エラーコード EINVAL がセットされ NULL を返します。 + * + * @param info リスト情報 + * @param index インデックス + * @return エントリ + */ +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index) +{ + + KcLinkedListEntry *entry = NULL; + kc_lock_guard(&(info->mutex)) + { + if (index > (int)info->size) + { // インデックス不正のためエラーを設定 + errno = EINVAL; + } + else if ((index == -1) || (index == (int)info->size)) + { // インデックス -1 or 末尾のため、tail を返す。 + entry = info->tail; + } + else + { + int half_size = info->size / 2; + if (index < half_size) + { // 前半のindexのため、前方より検索 + entry = info->head; + for (int idx = 0; idx <= index; idx++) + { + entry = entry->next; + } + } + else + { // 後半のindexのため、後方より検索 + // head 0 1 2 3 tail + entry = info->tail; + for (int idx = info->size; idx > index; idx--) + { + entry = entry->prev; + } + } + } + } + return entry; +} + +/** + * 新たなエントリを生成します。 + * 生成に失敗した場合、errno に ENOMEM がセットされ、NULL が返されます。 + * + * @param value 値 + * @param size サイズ + * @return 生成したエントリ + */ +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size) +{ + size_t entry_size = size + sizeof(KcLinkedListEntry); + KcLinkedListEntry *entry = (KcLinkedListEntry *)malloc(entry_size); + if (entry != NULL) + { + entry->value = (entry + 1); + entry->size = size; + memcpy(entry->value, value, size); + } + else + { // メモリ確保失敗 + errno = ENOMEM; + } + return entry; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For Quick Sort +// + +/** + * 指定されたエントリを入れ替えます。 + * 同じエントリが渡された場合は何もしません。 + * + * @param entry1 入れ替えるエントリ1 + * @param entry2 入れ替えるエントリ2 + */ +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2) +{ + if (entry1 != entry2) + { + KcLinkedListEntry *temp = entry1->next; + entry1->next = entry2->next; + entry2->next = temp; + temp = entry1->prev; + entry1->prev = entry2->prev; + entry2->prev = temp; + } +} + +/** + * Quick Sort パーティション処理 + * + * @param info LinkedList 情報 + * @param low Low + * @param high High + * @param comparator 比較関数 + * @param args 比較関数に渡されるデータ + */ +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListEntry *pivot = high; + KcLinkedListEntry *i = low->prev; + + for (KcLinkedListEntry *j = low; j != high; j = j->next) + { + int ret = comparator( + pivot->value, pivot->size, j->value, j->size, args); + if (ret >= 0) + { // j <= pivot + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, j); + } + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, high); + } + return i; +} + +/** + * Quick Sort 再帰処理 + */ +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + if ((high != info->tail) && (low != high) && (low != high->next)) + { + KcLinkedListEntry *p = KcLinkedList_sort_partition(info, low, high, comparator, args); + KcLinkedList_sort_recur(info, low, p->prev, comparator, args); + KcLinkedList_sort_recur(info, p->next, high, comparator, args); + } +} diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 3b807c0..5cc4186 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -24,7 +24,9 @@ // プロトタイプ宣言 // --- KcMemory -static void KcMemory_dump_leak(void); +// 確保中のメモリダンプは、 KcMemory_dump にて実施可能であり、 +// メモリリーク情報をダンプする KcMemory_dump_leak は明示的に公開はしない。 +void KcMemory_dump_leak(void); static bool KcMemory_print(const char *msg); // --- KcMemoryManager @@ -47,10 +49,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); static void *KcMemoryManager_reallocate_managed_ptr(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); static void KcMemoryManager_deallocate(void *ptr, KcMemoryMark expected_mark); //////////////////////////////////////////////////////////////////////////////// @@ -80,8 +78,6 @@ ._allocate = KcMemoryManager_allocate, ._reallocate = KcMemoryManager_reallocate, ._reallocate_managed_ptr = KcMemoryManager_reallocate_managed_ptr, - ._reallocate_invalid_ptr = KcMemoryManager_reallocate_invalid_ptr, - ._reallocate_unmanaged_ptr = KcMemoryManager_reallocate_unmanaged_ptr, ._deallocate = KcMemoryManager_deallocate, // --- 変数 --- @@ -116,7 +112,12 @@ void KcMemory_start(bool detail) { kc_memory_manager->_init(); - atexit(KcMemory_dump_leak); + static bool KcMemory_start = false; + if (!KcMemory_start) + { // atexit への登録は一度だけとする。 + atexit(KcMemory_dump_leak); + KcMemory_start = true; + } if (detail) { KcMemory_listener.allocate = KcMemoryListener_dump_allocate; @@ -142,7 +143,7 @@ /** * メモリリークしているメモリ情報をダンプします。 */ -static void KcMemory_dump_leak(void) +void KcMemory_dump_leak(void) { if (kc_memory_manager->_head._next != &(kc_memory_manager->_tail)) { @@ -316,7 +317,7 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 @@ -519,23 +520,9 @@ void *data_ptr = NULL; KcMemoryEntry *entry = (KcMemoryEntry *)ptr; entry--; - switch (entry->mark) + if (entry->mark == KC_MEMORY_ALLOCATED) { - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate - data_ptr = kc_memory_manager->_allocate(0, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED: // 管理されたメモリの realloc data_ptr = kc_memory_manager->_reallocate_managed_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - default: // 管理外メモリの realloc - data_ptr = kc_memory_manager->_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); - break; } return data_ptr; } @@ -568,7 +555,7 @@ KcMemoryEntry *new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); if (new_entry != NULL) { // メモリ確保成功 - kc_memory_manager->_listener.free(entry); + kc_memory_manager->_listener.free(new_entry); kc_memory_manager->_add(new_entry); kc_memory_manager->_listener.allocate(new_entry); data_ptr = new_entry->data; @@ -585,76 +572,6 @@ } // ------------------------------------- -// _reallocate_invalid_ptr -// ------------------------------------- -/** - * [内部利用関数] - * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - UNUSED_VARIABLE(ptr); - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate (invalid pointer)\n"); - errno = EINVAL; - return NULL; -} - -// ------------------------------------- -// _reallocate_unmanaged_ptr -// ------------------------------------- -/** - * [内部利用関数] - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - // | - // +----------+-------------------+ - // | 元の領域 | 追加分 + 管理領域 | - // +----------+-------------------+ - // ↓ - // ↓ memmove で 元の領域 + 追加分を、 - // ↓ 管理領域分を確保した先にコピーする - // ↓ - // +----------+----------+--------+ - // | 管理領域 | 元の領域 | 追加分 | - // +----------+----------+--------+ - void *data_ptr = NULL; - KcMemoryEntry *new_entry = KcMemoryEntry_new(ptr, 0, size, mark, file, func, line); - if (new_entry != NULL) - { // メモリ確保成功 - // memmove で 元の領域 + 追加分 をコピーして、メモリエントリとして追加する。 - memmove((new_entry + 1), new_entry, size); - kc_memory_manager->_add(new_entry); - kc_memory_manager->_listener.allocate(new_entry); - data_ptr = new_entry->data; - } - else - { // メモリ確保失敗 => エラー通知する。 - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate\n"); - } - return data_ptr; -} - -// ------------------------------------- // _deallocate // ------------------------------------- /** @@ -680,33 +597,64 @@ KcMemoryEntry_delete(entry); } else - { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 - switch (entry->mark) - { - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - case KC_MEMORY_ALLOCATED: // malloc 等で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use free)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete[])\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - default: // 管理外メモリ - free(ptr); - break; - } + { // 期待通りでない場合、管理外メモリのため stdlib.h の free にて解放する。 + raw_free(ptr); } } + +/** + * stdlib.h の malloc。 + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_malloc(size_t size) +{ + return malloc(size); +} + +/** + * stdlib.h の aligned_alloc。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} + +/** + * stdlib.h の calloc。 + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ +void *raw_calloc(size_t nmemb, size_t size) +{ + return calloc(nmemb, size); +} + +/** + * stdlib.h の realloc。 + * + * @param ptr メモリを再確保するポインタ + * @param size 確保しなおすメモリサイズ + * @return メモリへのポインタ + */ +void *raw_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +/** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ +void raw_free(void *ptr) +{ + free(ptr); +} diff --git a/modules/src/kc_memory_entry.c b/modules/src/kc_memory_entry.c index b855508..85f3fc2 100644 --- a/modules/src/kc_memory_entry.c +++ b/modules/src/kc_memory_entry.c @@ -5,9 +5,14 @@ */ #include +#include #include #include "kc_memory_entry_inner.h" +#ifdef KC_MEMORY_ENABLED +#undef KC_MEMORY_ENABLED +#endif + //////////////////////////////////////////////////////////////////////////////// // // 定数定義 @@ -16,6 +21,18 @@ /** パディング */ #define KC_MEMORY_PADDING (sizeof(void *) * 2) +//////////////////////////////////////////////////////////////////////////////// +// +// 変数定義 +// + +// For UNITTEST +#ifdef UNITTEST +bool (*_UT_KcMemory_can_alloc)( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) = NULL; +#endif // UNITTEST + /** * KcMemoryEntry を構築します。 * entry が NULL の場合、新規に KeMemoryEntry を構築します。 @@ -34,15 +51,22 @@ KcMemoryEntry *KcMemoryEntry_new(KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line) { +#ifdef UNITTEST + if (_UT_KcMemory_can_alloc && + !_UT_KcMemory_can_alloc(entry, alignment, size, mark, file, func, line)) + { + return NULL; + } +#endif KcMemoryEntry *new_entry; if ((entry == NULL) && (alignment > 0)) { // アライメント指定でメモリを確保する。 - new_entry = (KcMemoryEntry *)aligned_alloc(alignment, + new_entry = (KcMemoryEntry *)raw_aligned_alloc(alignment, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } else { - new_entry = (KcMemoryEntry *)realloc(entry, + new_entry = (KcMemoryEntry *)raw_realloc(entry, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } @@ -59,7 +83,7 @@ { entry->mark = KC_MEMORY_DELETED; entry->size = 0; - free(entry); + raw_free(entry); } /** diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index dcf722c..5bb141d 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -25,7 +25,7 @@ /** * テスト管理情報 */ -typedef struct +typedef struct KcUtInfo_ { int test_counter; //!< 実施テスト数 int ok_counter; //!< OK 件数 diff --git a/modules/test/src/test_assert.c b/modules/test/src/test_assert.c new file mode 100644 index 0000000..8d523f1 --- /dev/null +++ b/modules/test/src/test_assert.c @@ -0,0 +1,235 @@ +#include +#include + +#include +#include +#include + +// プロトタイプ宣言 +static void test_assert_handler(const char *msg); +static void test_assert_setup(void); +static void test_assert_teardown(void); +static void test_assert_off(void); +static void test_assert_on(void); + +static void test_assert_long(void); +static void test_assert_double(void); +static void test_assert_string(void); +static void test_assert_binary(void); +static void test_assert_null(void); +static void test_assert_true_false(void); + +/** + * assert 単体テストスイート + */ +void suite_assert(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_SETUP, "### SETUP (assert) ###", test_assert_setup); + ut->add(ut, UT_TESTCASE, "assert long", test_assert_long); + ut->add(ut, UT_TESTCASE, "assert double", test_assert_double); + ut->add(ut, UT_TESTCASE, "assert string", test_assert_string); + ut->add(ut, UT_TESTCASE, "assert binary", test_assert_binary); + ut->add(ut, UT_TESTCASE, "assert null", test_assert_null); + ut->add(ut, UT_TESTCASE, "assert true/false", test_assert_true_false); + ut->add(ut, UT_TEARDOWN, "### TEARDOWN (assert) ###", test_assert_teardown); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For assert_handler +// + +/** + * assert テスト用ハンドラが実行されたか否かを表します。 + */ +static bool test_assert_handler_pass = false; + +/** + * ut.c にて使われているハンドラバックアップ用。 + */ +static void (*test_assert_handler_backup)(const char *msg); + +/** + * assert テスト用のハンドラ。 + */ +static void test_assert_handler(const char *msg) +{ + UNUSED_VARIABLE(msg); + test_assert_handler_pass = true; +} + +/** + * assert テストのため、assert_handler を設定します。 + * ※assert にて失敗のテスト実施の際、ut 用のハンドラを使うとテストNGとなるため。 + */ +static void test_assert_setup(void) +{ + test_assert_handler_backup = assert_handler; +} + +/** + * assert テストのため、assert_handler を元にもどします。 + */ +static void test_assert_teardown(void) +{ + assert_handler = test_assert_handler_backup; +} + +/** + * 単体テストのエラー検出を無効にします。 + */ +static void test_assert_off(void) +{ + assert_handler = test_assert_handler; +} + +/** + * 単体テストのエラー検出を有効にします。 + */ +static void test_assert_on(void) +{ + assert_handler = test_assert_handler_backup; +} + +/** + * assert long 単体テスト。 + */ +static void test_assert_long(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(1234, 1234); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(1234, 1235); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert double 単体テスト。 + */ +static void test_assert_double(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 12.34, 0.1); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 12.35, 0.1); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 13.35, 0.1); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert string 単体テスト。 + */ +static void test_assert_string(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals("ABC", "ABC"); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals("ABC", "DEF"); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert binary 単体テスト。 + */ +static void test_assert_binary(void) +{ + test_assert_handler_pass = false; + char v1[] = {'A', 0, 'B', ' ', 'C', 0x81}; + char v2[] = {'A', 0, 'B', ' ', 'C', 0x81}; + char v3[] = {'A', 0, 'B', 0x19, 'C', 0x81}; + test_assert_off(); + assert_equals(v1, v2, 6); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(v1, v3, 6); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert null 単体テスト。 + */ +static void test_assert_null(void) +{ + const char *ptr = "ABC"; + const char *ptr_null = NULL; + + // not null + test_assert_handler_pass = false; + test_assert_off(); + assert_not_null(ptr); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + // null + test_assert_handler_pass = false; + test_assert_off(); + assert_null(ptr_null); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + // not null error + test_assert_handler_pass = false; + test_assert_off(); + assert_not_null(ptr_null); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + // null error + test_assert_handler_pass = false; + test_assert_off(); + assert_null(ptr); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert true/false 単体テスト。 + */ +static void test_assert_true_false(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_true(false); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_false(true); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_fail(); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} \ No newline at end of file diff --git a/modules/test/src/test_list_array.c b/modules/test/src/test_list_array.c index 5fa1256..1dec74e 100644 --- a/modules/test/src/test_list_array.c +++ b/modules/test/src/test_list_array.c @@ -17,7 +17,7 @@ static void test_list_array_malloc_error(void); /** - * memory_mark 単体テストスイート + * ArrayList 単体テストスイート */ void suite_list_array(void) { @@ -57,7 +57,8 @@ * @process 追加(index 不正) * @process 値取得(サイズ取得あり) * @process 値取得(サイズ取得なし) - * @process 値取得 (index 不正) + * @process 値取得 (index 不正[負の値を指定]) + * @process 値取得 (index 不正[サイズ以上を指定]) */ static void test_list_array_add(void) { @@ -114,7 +115,11 @@ res_val = list->get(list, 3, NULL); assert_equals(40, *res_val); - // 存在しないデータ取得 + // 値取得 (index 不正[負の値を指定]) + res_val = list->get(list, -1, NULL); + assert_null(res_val); + + // 値取得 (index 不正[サイズ以上を指定]) res_val = list->get(list, 4, NULL); assert_null(res_val); @@ -127,8 +132,10 @@ * @process 末尾削除 * @process 中間削除 * @process 先頭削除 - * @process 削除(index 不正) + * @process 削除(index 不正[負の値を指定]) + * @process 削除(index 不正[サイズ以上を指定]) * @process 残っているデータを確認 + * @process 容量調整(初期容量→1/4以下) */ static void test_list_array_remove(void) { @@ -175,7 +182,11 @@ now_size = list->size(list); assert_equals((default_size - 2), now_size); - // 削除(index 不正) + // 削除(index 不正[負の値指定]) + ret = list->remove(list, -1, NULL, NULL); + assert_false(ret); + + // 削除(index 不正[サイズ以上を指定]) ret = list->remove(list, default_size, NULL, NULL); assert_false(ret); @@ -206,10 +217,27 @@ assert_true(is_empty); KcList_delete(list); + + // 容量調整(初期容量→1/4以下) + KcList *big_list = KcList_new_ArrayList(sizeof(int), 12); + int big_val = 10; + // 追加 + bool big_ret = big_list->add(list, 0, &big_val, sizeof(int)); + assert_true(big_ret); + // 削除 (この時、容量調整発生) + big_ret = big_list->remove(list, 0, NULL, NULL); + assert_true(big_ret); + KcList_delete(big_list); } /** * ArrayList データ設定。 + * + * @process 値設定(複数設定) + * @process 値設定(元の値、サイズ取得) + * @process 値設定(元の値取得) + * @process 値設定(不正index[負の値指定]) + * @process 値設定(不正index[サイズ以上指定]) */ static void test_list_array_set(void) { @@ -246,14 +274,53 @@ res_val = list->get(list, 0, NULL); assert_equals(98, *res_val); + // 値設定 (サイズ取得) + set_value = 97; + ret = list->set(list, 0, &set_value, sizeof(int), NULL, &orig_size); + assert_true(ret); + assert_equals((int)sizeof(int), (int)orig_size); + res_val = list->get(list, 0, NULL); + assert_equals(97, *res_val); + now_size = list->size(list); assert_equals(5, now_size); + // 値設定(不正index[負の値指定]) + set_value = 96; + ret = list->set(list, -1, &set_value, sizeof(int), NULL, NULL); + assert_false(ret); + + // 値設定(不正index[サイズ以上指定]) + set_value = 95; + ret = list->set(list, 5, &set_value, sizeof(int), NULL, NULL); + assert_false(ret); + KcList_delete(list); } /** * ArrayList 検索。 + * + * @process リスト {10, 30, 50, 40, 20, 10, 30, 60, 20 } を構築する。 + * @result リストが構築されること + * + * @process is_contains でリストに含まれる値を確認する。 + * @result true が返されること。 + * + * @process index_of で各インデックス位置を取得する。 + * @result 先頭からのインデックス位置が正しく取得されること。 + * + * @process last_index_of で各インデックス位置を取得する。 + * @result 末尾からのインデックス位置が正しく取得されること。 + * + * @process リストに含まれない値を指定して、is_contains を実行する。 + * @result false が返されること。 + * + * @process リストに含まれない値を指定して、index_of を実行する。 + * @result -1 が返されること。 + * + * @process リストに含まれない値を指定して、last_index_of を実行する。 + * @result -1 が返されること。 */ static void test_list_array_search(void) { @@ -321,6 +388,15 @@ /** * ArrayList ソート。 + * + * @process リスト {10, 30, 50, 40, 20, 10, 30, 60, 20} を構築する。 + * @result リストが構築されること。 + * + * @process 昇順用コンパレータを渡し、ソートする。 + * @result リストがソートされること。 + * + * @process Iterator を取得する。 + * @result Iterator にて順次値が取得できること。 */ static void test_list_array_sort(void) { @@ -342,6 +418,7 @@ list->sort(list, test_list_array_sort_comparator, "ABCDEFG"); int sorted_vals[] = {10, 10, 20, 20, 30, 30, 40, 50, 60}; + // Iterator にて値を取得&確認 KcIterator *ite = list->iterator(list, 0); int sorted_index = 0; while (ite->hasNext(ite)) @@ -368,12 +445,71 @@ KcList_delete(list); } +#include "ut.h" +static int UT_ListArray_can_alloc_counter = 0; +static bool UT_ListArray_can_alloc( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) +{ + UNUSED_VARIABLE(entry); + UNUSED_VARIABLE(alignment); + UNUSED_VARIABLE(size); + UNUSED_VARIABLE(mark); + UNUSED_VARIABLE(file); + UNUSED_VARIABLE(func); + UNUSED_VARIABLE(line); + + UT_ListArray_can_alloc_counter--; + if (UT_ListArray_can_alloc_counter < 0) + { + return false; + } + return true; +} + /** * ArrayList メモリ確保失敗。 */ - static void test_list_array_malloc_error(void) { - KcList *list = KcList_new_ArrayList(sizeof(int), -1); + // ArrayList のメモリ確保失敗 + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + KcList *list = KcList_new_ArrayList(sizeof(int), 3); + _UT_KcMemory_can_alloc = NULL; assert_null(list); + + // ArrayList のデータ用メモリ確保失敗 + UT_ListArray_can_alloc_counter = 1; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + list = KcList_new_ArrayList(sizeof(int), 3); + _UT_KcMemory_can_alloc = NULL; + assert_null(list); + + // Array Size 拡張時のメモリ確保失敗 + list = KcList_new_ArrayList(sizeof(int), 3); + int val = 10; + list->add(list, 0, &val, sizeof(int)); + list->add(list, 1, &val, sizeof(int)); + list->add(list, 2, &val, sizeof(int)); + + bool ret; + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + // size 以上 + ret = list->add(list, 3, &val, sizeof(int)); + assert_false(ret); + // 0 指定 + ret = list->add(list, 0, &val, sizeof(int)); + assert_false(ret); + _UT_KcMemory_can_alloc = NULL; + + // Iterator 生成時のメモリ確保失敗 + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + KcIterator *ite = list->iterator(list, 0); + _UT_KcMemory_can_alloc = NULL; + assert_null(ite); + + KcList_delete(list); } \ No newline at end of file diff --git a/modules/test/src/test_memory.c b/modules/test/src/test_memory.c index ddf6d0d..b8b953e 100644 --- a/modules/test/src/test_memory.c +++ b/modules/test/src/test_memory.c @@ -1,33 +1,314 @@ #include +#include + #include #include #include +#include -static void test_1(void) -{ - assert_equals("a", "a"); -} +#include "ut.h" -static void test_2(void) -{ - assert_equals(1, 2); -} -static void setup(void) -{ - // NOP -} -static void test_3(void) -{ - assert_true(1 == 1); -} +// プロトタイプ宣言 +static void test_memory_start(void); +static void test_memory_dump(void); +static void test_memory_set_listener(void); +static void test_memory_entries(void); +static void test_memory_freeif(void); +static void test_memory_aligned(void); +static void test_memory_calloc(void); +static void test_memory_realloc(void); +static void test_memory_raw_xxxxx(void); -void test_memory(void) +/** + * memory_mark 単体テストスイート + */ +void suite_memory(void) { KcUt *ut = KcUt_get_instance(); - ut->add(ut, UT_SETUP, "SETUP", setup); - ut->add(ut, UT_TESTCASE, "テスト1", test_1); - ut->add(ut, UT_SETUP, "SETUP", setup); - ut->add(ut, UT_TESTCASE, "テスト2", test_2); - ut->add(ut, UT_SETUP, "SETUP", setup); - ut->add(ut, UT_TESTCASE, "テスト3", test_3); + ut->add(ut, UT_TESTCASE, "memory start", test_memory_start); + ut->add(ut, UT_TESTCASE, "memory dump", test_memory_dump); + ut->add(ut, UT_TESTCASE, "memory set_listener", test_memory_set_listener); + ut->add(ut, UT_TESTCASE, "memory entries", test_memory_entries); + ut->add(ut, UT_TESTCASE, "memory freeif", test_memory_freeif); + ut->add(ut, UT_TESTCASE, "memory aligned", test_memory_aligned); + ut->add(ut, UT_TESTCASE, "memory calloc", test_memory_calloc); + ut->add(ut, UT_TESTCASE, "memory realloc", test_memory_realloc); + ut->add(ut, UT_TESTCASE, "memory raw_xxxxx", test_memory_raw_xxxxx); +} + +/** + * メモリ管理開始。 + * + * @process メモリ管理開始(情報詳細出力あり) + * @process メモリ管理開始(情報詳細出力無し) + */ +static void test_memory_start(void) +{ + // NOP + // ut.c にて、下記実施 + // KcMemory_start(false); + // KcMemory_start(true); +} + +extern void KcMemory_dump_leak(void); +static bool test_memory_dump_handler(const char *data) +{ + printf("%s", data); + return true; +} + +/** + * 確保中のメモリ情報出力。 + * + * @process dump を実行する。 + * @result 確保中のメモリ情報が出力されること。 + * + * @param dump_leak を実行する。 + * @result メモリリーク情報が出力されること。 + * + * @process 確保しているメモリがない状態で dump を実行する。 + * @result メモリ情報が出力されないこと。 + * + * @process 出力用ハンドラを指定してメモリをダンプする。 + * @result ハンドラに対してメモリ情報が渡されること。 + */ +static void test_memory_dump(void) +{ + int *val = (int *)malloc(sizeof(int)); + *val = 10; + + printf("---DUMP---\n"); + KcMemory_dump(); + + printf("---DUMP LEAK---\n"); + KcMemory_dump_leak(); + + free(val); + printf("---DUMP---\n"); + KcMemory_dump(); + + val = (int *)malloc(sizeof(int)); + *val = 20; + kc_memory_manager->dump( + test_memory_dump_handler, 16, true, true, 8192); + free(val); +} + +/** + * リスナ設定(NULL指定)。 + * + * @process リスナを登録する。(NULL指定) + * @result リスナが設定されないこと。 + */ +static void test_memory_set_listener(void) +{ + KcMemoryListener listener; + listener.allocate = NULL; + listener.free = NULL; + listener.error = NULL; + kc_memory_manager->set_listener(&listener); +} + +// ハンドラ実行用 +static int test_memory_entries_handler_counter = 0; +static int test_memory_entries_handler_values[10]; +static int test_memory_entries_handler_can_loop = 0; +static bool test_memory_entries_handler(const KcMemoryEntry *entry, void *info) +{ + assert_equals( + test_memory_entries_handler_values[test_memory_entries_handler_counter], + *((int *)entry->data)); + test_memory_entries_handler_counter++; + test_memory_entries_handler_can_loop--; + assert_equals("X", (const char *)info); + if (test_memory_entries_handler_can_loop <= 0) + { + return false; + } + return true; +} +/** + * メモリエントリハンドラ実行 + */ +static void test_memory_entries(void) +{ + int *val1 = (int *)malloc(sizeof(int)); + int *val2 = (int *)malloc(sizeof(int)); + int *val3 = (int *)malloc(sizeof(int)); + *val1 = 123; + *val2 = 456; + *val3 = 789; + test_memory_entries_handler_values[0] = 123; + test_memory_entries_handler_values[1] = 456; + test_memory_entries_handler_values[2] = 789; + + // 途中中断パターン + test_memory_entries_handler_counter = 0; + test_memory_entries_handler_can_loop = 2; + kc_memory_manager->entries(test_memory_entries_handler, "X"); + assert_equals(2, test_memory_entries_handler_counter); + + // 全てダンプパターン + test_memory_entries_handler_counter = 0; + test_memory_entries_handler_can_loop = 999; + kc_memory_manager->entries(test_memory_entries_handler, "X"); + assert_equals(3, test_memory_entries_handler_counter); + + free(val1); + free(val2); + free(val3); +} + +bool test_memory_freeif_handler(const KcMemoryEntry *entry, void *info) +{ + assert_equals("FREEIF", (const char *)info); + if (strcmp("test_memory_freeif", entry->func) == 0) + { + int *data = (int *)entry->data; + if (*data == 456) + { + return true; + } + } + return false; +} + +/** + * 強制メモリ破棄 + */ +static void test_memory_freeif(void) +{ + int *val1 = (int *)malloc(sizeof(int)); + int *val2 = (int *)malloc(sizeof(int)); + int *val3 = (int *)malloc(sizeof(int)); + *val1 = 123; + *val2 = 456; + *val3 = 789; + test_memory_entries_handler_values[0] = 123; + test_memory_entries_handler_values[1] = 456; + test_memory_entries_handler_values[2] = 789; + + kc_memory_manager->freeif(test_memory_freeif_handler, "FREEIF"); + free(val1); + // free(val2); + free(val3); +} + +// メモリ確保失敗動作確認用 +static int UT_Memory_can_alloc_counter = 0; +static bool UT_Memory_can_alloc( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) +{ + UNUSED_VARIABLE(entry); + UNUSED_VARIABLE(alignment); + UNUSED_VARIABLE(size); + UNUSED_VARIABLE(mark); + UNUSED_VARIABLE(file); + UNUSED_VARIABLE(func); + UNUSED_VARIABLE(line); + + UT_Memory_can_alloc_counter--; + if (UT_Memory_can_alloc_counter < 0) + { + return false; + } + return true; +} +/** + * アライメント指定メモリ確保 + * + * @process aligned_alloc によりメモリを確保する。 + * @result メモリが確保されること。 + * + * @process メモリ確保が失敗する状態で、aligned_alloc を実行する。 + * @result メモリ確保に失敗し、NULL が返されること。 + */ +static void test_memory_aligned(void) +{ + int *val = (int *)aligned_alloc(sizeof(long long), sizeof(int)); + assert_not_null(val); + *val = 123; + free(val); + + UT_Memory_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_Memory_can_alloc; + val = (int *)aligned_alloc(sizeof(long long), sizeof(int)); + assert_null(val); + _UT_KcMemory_can_alloc = NULL; +} + +/** + * calloc によるメモリ確保 + * + * @process calloc によりメモリを確保する。 + * @result メモリが確保されること。内容がすべて 0 のこと。 + * + * @process メモリ確保が失敗する状態で、calloc を実行する。 + * @result メモリ確保に失敗し、NULL が返されること。 + */ +static void test_memory_calloc(void) +{ + int *val = (int *)calloc(10, sizeof(int)); + for (int i = 0; i < 10; i++) + { + assert_equals(0, val[i]); + } + free(val); + + UT_Memory_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_Memory_can_alloc; + val = (int *)calloc(10, sizeof(int)); + assert_null(val); + _UT_KcMemory_can_alloc = NULL; +} + +/** + * realloc によるメモリ確保 + * + * @process NULL を指定して、realloc によりメモリを確保する。 + * @result メモリが確保されること。 + * + * @process メモリ確保が失敗する状態で、calloc を実行する。 + * @result メモリ確保に失敗し、NULL が返されること。 + */ +static void test_memory_realloc(void) +{ + // NULL 指定 + int *val = realloc(NULL, sizeof(int)); + assert_not_null(val); + *val = 123; + free(val); + + // 管理外メモリによる realloc + int *unmng_ptr = raw_malloc(sizeof(int) * 2); + unmng_ptr[0] = 123; + unmng_ptr[1] = 456; + assert_equals(123, unmng_ptr[0]); + assert_equals(456, unmng_ptr[1]); + + int *mng_ptr = (int *)realloc(unmng_ptr, sizeof(int) * 3); + assert_null(mng_ptr); + free(unmng_ptr); +} + +/** + * raw_xxxxx 関数動作確認。 + */ +static void test_memory_raw_xxxxx(void) +{ + int *ptr = (int *)raw_malloc(sizeof(int)); + assert_not_null(ptr); + raw_free(ptr); + + ptr = raw_aligned_alloc(sizeof(int), sizeof(int)); + assert_not_null(ptr); + raw_free(ptr); + + ptr = raw_calloc(10, sizeof(int)); + assert_not_null(ptr); + + ptr = raw_realloc(ptr, 20); + assert_not_null(ptr); + raw_free(ptr); } diff --git a/modules/test/src/ut.c b/modules/test/src/ut.c index 41f9017..ed34d55 100644 --- a/modules/test/src/ut.c +++ b/modules/test/src/ut.c @@ -3,26 +3,22 @@ #include #include -extern void suite_lock_guard(void); -extern void suite_memory_dump(void); -extern void suite_memory_entry(void); -extern void suite_memory_listener(void); -extern void suite_memory_mark(void); -extern void suite_list_array(void); +#include "ut.h" int main(void) { // UT Setup - // test_list(); - // test_memory(); + suite_assert(); + suite_list_array(); suite_lock_guard(); suite_memory_dump(); suite_memory_entry(); suite_memory_listener(); suite_memory_mark(); - suite_list_array(); + suite_memory(); KcMemory_start(false); + KcMemory_start(true); KcUt *ut = KcUt_get_instance(); ut->run(ut); diff --git a/modules/include/kc_memory.h b/modules/include/kc_memory.h index 3ca9321..ffce4ed 100644 --- a/modules/include/kc_memory.h +++ b/modules/include/kc_memory.h @@ -145,14 +145,14 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 * @return 確保したメモリへのポインタ */ - void *(*aligned_alloc)(size_t alignement, size_t size, const char *file, const char *func, int line); + void *(*aligned_alloc)(size_t alignment, size_t size, const char *file, const char *func, int line); /** * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。 @@ -206,10 +206,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); void *(*_reallocate_managed_ptr)(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_invalid_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); - void *(*_reallocate_unmanaged_ptr)(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); void (*_deallocate)(void *ptr, KcMemoryMark expected_mark); // ========================================================================= @@ -229,8 +225,51 @@ */ extern KcMemoryManager *const kc_memory_manager; + /** + * stdlib.h の malloc + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_malloc(size_t size); + + /** + * stdlib.h の aligned_alloc + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_aligned_alloc(size_t alignment, size_t size); + + /** + * stdlib.h の calloc + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ + void *raw_calloc(size_t nmemb, size_t size); + + /** + * stdlib.h の realloc + * + * @param ptr 再確保するためのメモリへのポインタ + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ + void *raw_realloc(void *ptr, size_t size); + + /** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr); + #ifdef KC_MEMORY_ENABLED #define malloc(size) kc_memory_manager->malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) kc_memory_manager->aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kc_memory_manager->realloc(ptr, size, __FILE__, __func__, __LINE__) #define free(ptr) kc_memory_manager->free(ptr) diff --git a/modules/src/kc_list_array.c b/modules/src/kc_list_array.c index fd0beab..99a171f 100644 --- a/modules/src/kc_list_array.c +++ b/modules/src/kc_list_array.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,7 @@ * * @param size 要素のサイズ * @param cap リストの初期容量 - * @param file ファイル - * @param func 関数 - * @param line 行番号 + * @return ArrayList */ KcList *KcList_new_ArrayList(size_t size, int cap) { @@ -174,15 +173,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; bool is_contains = false; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { is_contains = true; break; @@ -213,12 +209,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; + // insert_index は常に 0 以上 int insert_index = (index < 0) ? info->size : index; bool is_success = true; kc_lock_guard(&(info->mutex)) { - is_success = ((0 <= insert_index) && (insert_index <= info->size)); - is_success = is_success && KcArrayList_increase_capacity(info); + is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info); element_type *info_data = (element_type *)info->data; if (is_success) { @@ -257,7 +253,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -323,7 +319,6 @@ * @param list 対象リスト * @param comparator リスト要素を比較するために使用される comparator * @param args comparator の第5引数に渡すオブジェクト - * @return true/false (ソート成功/ソート失敗) */ static void KcArrayList_sort(KcList *list, int (*comparator)(const void *element1, size_t size1, @@ -397,6 +392,10 @@ *size = info->element_size; } } + else + { + errno = EINVAL; + } } return res; } @@ -426,7 +425,7 @@ typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - bool is_success = true; + bool is_success = false; kc_lock_guard(&(info->mutex)) { is_success = ((0 <= index) && (index < info->size)); @@ -465,15 +464,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = 0; idx < info->size; idx++) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; @@ -502,15 +498,12 @@ KcArrayListInfo *info = (KcArrayListInfo *)list->_info; typedef uint8_t element_type[info->element_size]; element_type *info_data = (element_type *)info->data; - - int res; int result_index = -1; kc_lock_guard(&(info->mutex)) { for (int idx = (info->size - 1); idx >= 0; idx--) { - res = memcmp(&info_data[idx], element, info->element_size); - if (res == 0) + if (memcmp(&info_data[idx], element, info->element_size) == 0) { result_index = idx; break; diff --git a/modules/src/kc_list_linked.c b/modules/src/kc_list_linked.c new file mode 100644 index 0000000..9e746a1 --- /dev/null +++ b/modules/src/kc_list_linked.c @@ -0,0 +1,721 @@ +/** + * @file kc_list_linked.c + * @brief Linked リストモジュール + * @copyright 2003 - 2023 Nomura Kei + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * KcLinkedList Entry 情報 + */ +typedef struct KcLinkedListEntry_ +{ + void *value; //!< 値 + size_t size; //!< 値のサイズ + struct KcLinkedListEntry_ *next; //!< 次のエントリ + struct KcLinkedListEntry_ *prev; //!< 前のエントリ +} KcLinkedListEntry; + +/** + * KcLinkedList 管理情報 + */ +typedef struct +{ + mtx_t mutex; //!< ロック用 + size_t size; //!< エントリ数 + KcLinkedListEntry *head; //!< 先頭エントリ + KcLinkedListEntry *tail; //!< 末尾エントリ +} KcLinkedListInfo; + +// ============================================================================= +// プロトタイプ宣言 +// ============================================================================= +static int KcLinkedList_size(KcList *list); +static bool KcLinkedList_is_empty(KcList *list); +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size); +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size); +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size); +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_clear(KcList *list); +static void *KcLinkedList_get(KcList *list, int index, size_t *size); +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size); +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size); +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size); +static KcIterator *KcLinkedList_iterator(KcList *list, int index); +static void KcLinkedList_cleanup_info(KcList *list); + +// [内部関数用] +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index); +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size); + +// [内部関数用] For Quick Sort +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2); +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, KcLinkedListEntry *low, KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args); + +/** + * LinkedList を構築します。 + * + * @return LinkedList + */ +KcList *KcList_new_LinkedList(void) +{ + // KcLinkedList の管理構造 + // +--------------+ + // | KcList | + // | ... | + // | _info -----------+ + // +--------------+ | + // | <_info> | <---+ + // | next --------+ + // | prev --------|--+ + // +--------------+ | | + // | |<-+ | + // | |<----+ + // +--------------+ + KcList *list = (KcList *)malloc( + sizeof(KcList) + sizeof(KcLinkedListInfo) + (sizeof(KcLinkedListEntry) * 2)); + if (list != NULL) + { + list->size = KcLinkedList_size; + list->is_empty = KcLinkedList_is_empty; + list->contains = KcLinkedList_contains; + list->add = KcLinkedList_add; + list->remove = KcLinkedList_remove; + list->sort = KcLinkedList_sort; + list->clear = KcLinkedList_clear; + list->get = KcLinkedList_get; + list->set = KcLinkedList_set; + list->index_of = KcLinkedList_index_of; + list->last_index_of = KcLinkedList_last_index_of; + list->iterator = KcLinkedList_iterator; + list->cleanup_info = KcLinkedList_cleanup_info; + list->_info = (list + 1); + + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + mtx_init(&(info->mutex), mtx_plain | mtx_recursive); + info->head = (KcLinkedListEntry *)(info + 1); + info->tail = info->head + 1; + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } + return list; +} + +// ----------------------------------------------------------------------------- +// size +// ----------------------------------------------------------------------------- +/** + * 対象リスト内にある要素の数を返します。 + * + * @param list 対象リスト + * @return 対象リスト内の要素数 + */ +static int KcLinkedList_size(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int size = -1; + kc_lock_guard(&(info->mutex)) + { + size = info->size; + } + return size; +} + +// ----------------------------------------------------------------------------- +// is_empty +// ----------------------------------------------------------------------------- +/** + * 対象リストに要素がない場合に true を返します。 + * + * @param list 対象リスト + * @return 対象リストに要素が含まれていない場合は true + */ +static bool KcLinkedList_is_empty(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_empty = true; + kc_lock_guard(&(info->mutex)) + { + is_empty = (info->size == 0); + } + return is_empty; +} + +// ----------------------------------------------------------------------------- +// contains +// ----------------------------------------------------------------------------- +/** + * 指定の要素が対象リストに含まれている場合に true を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 対象リスト内にあるかどうか判定される要素 + * @param size 要素のサイズ + * @return 指定された要素が対象リスト内にある場合は true + */ +static bool KcLinkedList_contains(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_contains = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = info->head->next; + while (entry != info->tail) + { + if ((entry->size == size) && (memcmp(entry->value, element, size) == 0)) + { + is_contains = true; + break; + } + entry = entry->next; + } + } + return is_contains; +} + +// ----------------------------------------------------------------------------- +// add +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置に、指定された要素を挿入します。 + * index に負値を指定した場合、末尾に要素を追加します。 + * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param index 指定の要素が挿入される位置のインデックス + * @param element 挿入される要素 + * @param size 要素のサイズ + * @return true/false (格納成功/失敗) + */ +static bool KcLinkedList_add(KcList *list, int index, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *insert_pos = KcLinkedList_search(info, index); + if (insert_pos != NULL) + { // エントリ作成 + KcLinkedListEntry *entry = KcLinkedList_new_entry(element, size); + if (entry != NULL) + { // リストに追加 (insert_pos の一つ前に追加) + info->size++; + entry->next = insert_pos; + entry->prev = insert_pos->prev; + insert_pos->prev->next = entry; + insert_pos->prev = entry; + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// remove +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を削除します。 + * 後続の要素を左に移動します(インデックスから1を減算)。 + * element と size が共に NULL でない場合、削除された要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 削除される要素のインデックス + * @param element 削除された要素をコピーが格納されます。 + * @param size element のバッファサイズを指定します。また、削除された要素のサイズが格納されます。 + * @return true/false (削除成功/失敗) + */ +static bool KcLinkedList_remove(KcList *list, int index, void *element, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = false; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + if ((element != NULL) && (size != NULL)) + { + size_t copy_size = (entry->size < *size) ? entry->size : *size; + memcpy(element, entry->value, copy_size); + *size = entry->size; + } + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + free(entry); + info->size--; + is_success = true; + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// sort +// ----------------------------------------------------------------------------- + +/** + * 指定された comparator が示す順序に従って、対象リストをソートします。 + * + * @param list 対象リスト + * @param comparator リスト要素を比較するために使用される comparator + * @param args comparator の第5引数に渡すオブジェクト + * @return true/false (ソート成功/ソート失敗) + */ +static void KcLinkedList_sort(KcList *list, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedList_sort_recur( + info, + info->head->next, + info->tail->prev, + comparator, args); + } +} + +// ----------------------------------------------------------------------------- +// clear +// ----------------------------------------------------------------------------- +/** + * すべての要素を対象リストから削除します。 + * + * @param list 対象リスト + */ +static void KcLinkedList_clear(KcList *list) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry; + entry = info->head->next; + while (entry != info->tail) + { + entry = entry->next; + free(entry->prev); + } + info->size = 0; + info->head->next = info->head->prev = info->tail; + info->tail->next = info->tail->prev = info->head; + info->head->value = info->tail->value = NULL; + } +} + +// ----------------------------------------------------------------------------- +// get +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を返します。 + * + * @param list 対象リスト + * @param index 取得する要素のインデックス + * @param size 要素のサイズを格納するポインタ + * @return 対象リスト内の指定された位置にある要素 + */ +static void *KcLinkedList_get(KcList *list, int index, size_t *size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + + void *res = NULL; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *entry = KcLinkedList_search(info, index); + if ((entry != NULL) && (entry != info->tail)) + { + res = entry->value; + if (size) + { + *size = entry->size; + } + } + else + { + errno = EINVAL; + } + } + return res; +} + +// ----------------------------------------------------------------------------- +// set +// ----------------------------------------------------------------------------- +/** + * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * org_element と org_size が共にNULL でない場合、置き換え前の要素のコピーが格納されます。 + * + * @param list 対象リスト + * @param index 置換される要素のインデックス + * @param element 指定された位置に格納される要素 + * @param size 指定された位置に格納される要素のサイズ + * @param org_element 指定された位置に以前あった要素のコピーが格納されます。 + * @param org_size org_element のサイズを指定します。また、指定された位置に以前あった要素のサイズが格納されます。 + * @return true/false (置換成功/失敗) + */ +static bool KcLinkedList_set(KcList *list, int index, const void *element, size_t size, + void *org_element, size_t *org_size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + bool is_success = true; + kc_lock_guard(&(info->mutex)) + { + KcLinkedListEntry *remove_entry = KcLinkedList_search(info, index); + if ((remove_entry != NULL) && (remove_entry != info->tail)) + { + KcLinkedListEntry *new_entry = KcLinkedList_new_entry(element, size); + if (new_entry) + { + if ((org_element != NULL) && (org_size != NULL)) + { + size_t copy_size = (remove_entry->size < *org_size) ? remove_entry->size : *org_size; + memcpy(org_element, remove_entry->value, copy_size); + *org_size = remove_entry->size; + } + // 削除する位置に新たなエントリを追加 + new_entry->next = remove_entry->next; + new_entry->prev = remove_entry->prev; + remove_entry->prev->next = new_entry; + remove_entry->next->prev = new_entry; + free(remove_entry); + is_success = true; + } + } + } + return is_success; +} + +// ----------------------------------------------------------------------------- +// index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス + */ +static int KcLinkedList_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->head->next; entry != info->tail; entry = entry->next) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +// ----------------------------------------------------------------------------- +// last_index_of +// ----------------------------------------------------------------------------- +/** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * 要素サイズ固定のリストを扱う場合、size の値は無視されます。 + * + * @param list 対象リスト + * @param element 検索する要素 + * @param size 検索する要素のサイズ + * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス + */ +static int KcLinkedList_last_index_of(KcList *list, const void *element, size_t size) +{ + KcLinkedListInfo *info = (KcLinkedListInfo *)list->_info; + int result_index = -1; + kc_lock_guard(&(info->mutex)) + { + int idx = 0; + for (KcLinkedListEntry *entry = info->tail->prev; entry != info->head; entry = entry->prev) + { + if ((size == entry->size) && (memcmp(entry->value, element, entry->size) == 0)) + { + result_index = idx; + break; + } + entry = entry->next; + idx++; + } + } + return result_index; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Iterator +// + +/** + * LinkedListe の Iterator 管理用構造体 + */ +typedef struct KcLinkedListIteratorEntry_ +{ + KcLinkedListInfo *info; //!< ArrayList情報 + KcLinkedListEntry *current; //!< 現在のエントリ +} KcLinkedListIteratorEntry; + +/** + * 次の要素があるか否かを返します。 + * + * @param ite Iterator + * @return true/false (次の要素が存在する/存在しない) + */ +static bool KcLinkedListIterator_hasNext(KcIterator *ite) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + return (entry->current != entry->info->tail); +} + +/** + * 次の要素を取得します。 + * size が指定されている場合、次の要素が size に格納されます。 + * 次の要素がない場合、NULL を返します。 + * + * @param ite Iterator + * @param size 要素のサイズ格納用 + * @return 次の要素 + */ +static const void *KcLinkedListIterator_next(KcIterator *ite, size_t *size) +{ + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)ite->_info; + if (size != NULL) + { + *size = entry->current->size; + } + entry->current = entry->current->next; + return (entry->current->prev->value); +} + +/** + * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。 + * + * @param list 対象リスト + * @param index イテレータから返される最初の要素のインデックス + * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ + */ +static KcIterator *KcLinkedList_iterator(KcList *list, int index) +{ + size_t ite_size = sizeof(KcIterator) + sizeof(KcLinkedListIteratorEntry); + KcIterator *ite = (KcIterator *)malloc(ite_size); + if (ite) + { + ite->hasNext = KcLinkedListIterator_hasNext; + ite->next = KcLinkedListIterator_next; + KcLinkedListIteratorEntry *entry = (KcLinkedListIteratorEntry *)(((KcIterator *)ite) + 1); + ite->_info = entry; + + entry->info = list->_info; + entry->current = KcLinkedList_search(entry->info, index); + } + return ite; +} + +/** + * リスト情報をクリアします。 + * + * @param list 対象リスト + */ +static void KcLinkedList_cleanup_info(KcList *list) +{ + list->clear(list); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// 内部関数 +// + +/** + * [内部関数] リストより指定されたインデックスのエントリを取得します。 + * インデックスが -1 または、現在の size 位置の場合、 tail を返します。 + * インデックス位置が不正な場合、エラーコード EINVAL がセットされ NULL を返します。 + * + * @param info リスト情報 + * @param index インデックス + * @return エントリ + */ +static KcLinkedListEntry *KcLinkedList_search(KcLinkedListInfo *info, int index) +{ + + KcLinkedListEntry *entry = NULL; + kc_lock_guard(&(info->mutex)) + { + if (index > (int)info->size) + { // インデックス不正のためエラーを設定 + errno = EINVAL; + } + else if ((index == -1) || (index == (int)info->size)) + { // インデックス -1 or 末尾のため、tail を返す。 + entry = info->tail; + } + else + { + int half_size = info->size / 2; + if (index < half_size) + { // 前半のindexのため、前方より検索 + entry = info->head; + for (int idx = 0; idx <= index; idx++) + { + entry = entry->next; + } + } + else + { // 後半のindexのため、後方より検索 + // head 0 1 2 3 tail + entry = info->tail; + for (int idx = info->size; idx > index; idx--) + { + entry = entry->prev; + } + } + } + } + return entry; +} + +/** + * 新たなエントリを生成します。 + * 生成に失敗した場合、errno に ENOMEM がセットされ、NULL が返されます。 + * + * @param value 値 + * @param size サイズ + * @return 生成したエントリ + */ +static KcLinkedListEntry *KcLinkedList_new_entry(const void *value, size_t size) +{ + size_t entry_size = size + sizeof(KcLinkedListEntry); + KcLinkedListEntry *entry = (KcLinkedListEntry *)malloc(entry_size); + if (entry != NULL) + { + entry->value = (entry + 1); + entry->size = size; + memcpy(entry->value, value, size); + } + else + { // メモリ確保失敗 + errno = ENOMEM; + } + return entry; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For Quick Sort +// + +/** + * 指定されたエントリを入れ替えます。 + * 同じエントリが渡された場合は何もしません。 + * + * @param entry1 入れ替えるエントリ1 + * @param entry2 入れ替えるエントリ2 + */ +static void KcLinkedList_swap( + KcLinkedListEntry *entry1, KcLinkedListEntry *entry2) +{ + if (entry1 != entry2) + { + KcLinkedListEntry *temp = entry1->next; + entry1->next = entry2->next; + entry2->next = temp; + temp = entry1->prev; + entry1->prev = entry2->prev; + entry2->prev = temp; + } +} + +/** + * Quick Sort パーティション処理 + * + * @param info LinkedList 情報 + * @param low Low + * @param high High + * @param comparator 比較関数 + * @param args 比較関数に渡されるデータ + */ +static KcLinkedListEntry *KcLinkedList_sort_partition( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + KcLinkedListEntry *pivot = high; + KcLinkedListEntry *i = low->prev; + + for (KcLinkedListEntry *j = low; j != high; j = j->next) + { + int ret = comparator( + pivot->value, pivot->size, j->value, j->size, args); + if (ret >= 0) + { // j <= pivot + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, j); + } + i = (i == info->head) ? low : i->next; + KcLinkedList_swap(i, high); + } + return i; +} + +/** + * Quick Sort 再帰処理 + */ +static void KcLinkedList_sort_recur( + KcLinkedListInfo *info, + KcLinkedListEntry *low, + KcLinkedListEntry *high, + int (*comparator)(const void *element1, size_t size1, + const void *element2, size_t size2, void *args), + void *args) +{ + if ((high != info->tail) && (low != high) && (low != high->next)) + { + KcLinkedListEntry *p = KcLinkedList_sort_partition(info, low, high, comparator, args); + KcLinkedList_sort_recur(info, low, p->prev, comparator, args); + KcLinkedList_sort_recur(info, p->next, high, comparator, args); + } +} diff --git a/modules/src/kc_memory.c b/modules/src/kc_memory.c index 3b807c0..5cc4186 100644 --- a/modules/src/kc_memory.c +++ b/modules/src/kc_memory.c @@ -24,7 +24,9 @@ // プロトタイプ宣言 // --- KcMemory -static void KcMemory_dump_leak(void); +// 確保中のメモリダンプは、 KcMemory_dump にて実施可能であり、 +// メモリリーク情報をダンプする KcMemory_dump_leak は明示的に公開はしない。 +void KcMemory_dump_leak(void); static bool KcMemory_print(const char *msg); // --- KcMemoryManager @@ -47,10 +49,6 @@ KcMemoryMark mark, const char *file, const char *func, int line); static void *KcMemoryManager_reallocate_managed_ptr(void *ptr, size_t size, KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line); static void KcMemoryManager_deallocate(void *ptr, KcMemoryMark expected_mark); //////////////////////////////////////////////////////////////////////////////// @@ -80,8 +78,6 @@ ._allocate = KcMemoryManager_allocate, ._reallocate = KcMemoryManager_reallocate, ._reallocate_managed_ptr = KcMemoryManager_reallocate_managed_ptr, - ._reallocate_invalid_ptr = KcMemoryManager_reallocate_invalid_ptr, - ._reallocate_unmanaged_ptr = KcMemoryManager_reallocate_unmanaged_ptr, ._deallocate = KcMemoryManager_deallocate, // --- 変数 --- @@ -116,7 +112,12 @@ void KcMemory_start(bool detail) { kc_memory_manager->_init(); - atexit(KcMemory_dump_leak); + static bool KcMemory_start = false; + if (!KcMemory_start) + { // atexit への登録は一度だけとする。 + atexit(KcMemory_dump_leak); + KcMemory_start = true; + } if (detail) { KcMemory_listener.allocate = KcMemoryListener_dump_allocate; @@ -142,7 +143,7 @@ /** * メモリリークしているメモリ情報をダンプします。 */ -static void KcMemory_dump_leak(void) +void KcMemory_dump_leak(void) { if (kc_memory_manager->_head._next != &(kc_memory_manager->_tail)) { @@ -316,7 +317,7 @@ /** * アライメント指定付きで、指定されたサイズのメモリを確保します。 * - * @param alignemnt アライメント + * @param alignment アライメント * @param size 確保するメモリサイズ * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 @@ -519,23 +520,9 @@ void *data_ptr = NULL; KcMemoryEntry *entry = (KcMemoryEntry *)ptr; entry--; - switch (entry->mark) + if (entry->mark == KC_MEMORY_ALLOCATED) { - case KC_MEMORY_DELETED: // 削除済みメモリ => 通常の allocate - data_ptr = kc_memory_manager->_allocate(0, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED: // 管理されたメモリの realloc data_ptr = kc_memory_manager->_reallocate_managed_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリの realloc のためエラー - data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line); - break; - default: // 管理外メモリの realloc - data_ptr = kc_memory_manager->_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line); - break; } return data_ptr; } @@ -568,7 +555,7 @@ KcMemoryEntry *new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line); if (new_entry != NULL) { // メモリ確保成功 - kc_memory_manager->_listener.free(entry); + kc_memory_manager->_listener.free(new_entry); kc_memory_manager->_add(new_entry); kc_memory_manager->_listener.allocate(new_entry); data_ptr = new_entry->data; @@ -585,76 +572,6 @@ } // ------------------------------------- -// _reallocate_invalid_ptr -// ------------------------------------- -/** - * [内部利用関数] - * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_invalid_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - UNUSED_VARIABLE(ptr); - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate (invalid pointer)\n"); - errno = EINVAL; - return NULL; -} - -// ------------------------------------- -// _reallocate_unmanaged_ptr -// ------------------------------------- -/** - * [内部利用関数] - * 管理外メモリ領域に対する realloc を実施します。 - * - * @param ptr ポインタ - * @param size 確保するメモリサイズ - * @param file メモリ確保ファイル名 - * @param func メモリ確保関数名 - * @param line メモリ確保行番号 - * @return 確保したメモリへのポインタ - */ -static void *KcMemoryManager_reallocate_unmanaged_ptr(void *ptr, size_t size, - KcMemoryMark mark, const char *file, const char *func, int line) -{ - // | - // +----------+-------------------+ - // | 元の領域 | 追加分 + 管理領域 | - // +----------+-------------------+ - // ↓ - // ↓ memmove で 元の領域 + 追加分を、 - // ↓ 管理領域分を確保した先にコピーする - // ↓ - // +----------+----------+--------+ - // | 管理領域 | 元の領域 | 追加分 | - // +----------+----------+--------+ - void *data_ptr = NULL; - KcMemoryEntry *new_entry = KcMemoryEntry_new(ptr, 0, size, mark, file, func, line); - if (new_entry != NULL) - { // メモリ確保成功 - // memmove で 元の領域 + 追加分 をコピーして、メモリエントリとして追加する。 - memmove((new_entry + 1), new_entry, size); - kc_memory_manager->_add(new_entry); - kc_memory_manager->_listener.allocate(new_entry); - data_ptr = new_entry->data; - } - else - { // メモリ確保失敗 => エラー通知する。 - KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line); - kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate\n"); - } - return data_ptr; -} - -// ------------------------------------- // _deallocate // ------------------------------------- /** @@ -680,33 +597,64 @@ KcMemoryEntry_delete(entry); } else - { // 期待通りでない場合、メモリ状態に応じて警告を通知する。 - switch (entry->mark) - { - case KC_MEMORY_DELETED: // 削除済みメモリ - // Nothing to do. - break; - case KC_MEMORY_ALLOCATED: // malloc 等で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use free)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW: // new で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete)\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - case KC_MEMORY_ALLOCATED_NEW_ARRAY: // new[] で確保されたメモリ - kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete[])\n"); - kc_memory_manager->_listener.free(entry); - kc_memory_manager->_remove(entry); - KcMemoryEntry_delete(entry); - break; - default: // 管理外メモリ - free(ptr); - break; - } + { // 期待通りでない場合、管理外メモリのため stdlib.h の free にて解放する。 + raw_free(ptr); } } + +/** + * stdlib.h の malloc。 + * + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_malloc(size_t size) +{ + return malloc(size); +} + +/** + * stdlib.h の aligned_alloc。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @return メモリへのポインタ + */ +void *raw_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} + +/** + * stdlib.h の calloc。 + * + * @param nmemb 個数 + * @param size 要素のサイズ + * @return メモリへのポインタ + */ +void *raw_calloc(size_t nmemb, size_t size) +{ + return calloc(nmemb, size); +} + +/** + * stdlib.h の realloc。 + * + * @param ptr メモリを再確保するポインタ + * @param size 確保しなおすメモリサイズ + * @return メモリへのポインタ + */ +void *raw_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +/** + * stdlib.h の free + * + * @param ptr 解放するメモリへのポインタ + */ +void raw_free(void *ptr) +{ + free(ptr); +} diff --git a/modules/src/kc_memory_entry.c b/modules/src/kc_memory_entry.c index b855508..85f3fc2 100644 --- a/modules/src/kc_memory_entry.c +++ b/modules/src/kc_memory_entry.c @@ -5,9 +5,14 @@ */ #include +#include #include #include "kc_memory_entry_inner.h" +#ifdef KC_MEMORY_ENABLED +#undef KC_MEMORY_ENABLED +#endif + //////////////////////////////////////////////////////////////////////////////// // // 定数定義 @@ -16,6 +21,18 @@ /** パディング */ #define KC_MEMORY_PADDING (sizeof(void *) * 2) +//////////////////////////////////////////////////////////////////////////////// +// +// 変数定義 +// + +// For UNITTEST +#ifdef UNITTEST +bool (*_UT_KcMemory_can_alloc)( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) = NULL; +#endif // UNITTEST + /** * KcMemoryEntry を構築します。 * entry が NULL の場合、新規に KeMemoryEntry を構築します。 @@ -34,15 +51,22 @@ KcMemoryEntry *KcMemoryEntry_new(KcMemoryEntry *entry, size_t alignment, size_t size, KcMemoryMark mark, const char *file, const char *func, int line) { +#ifdef UNITTEST + if (_UT_KcMemory_can_alloc && + !_UT_KcMemory_can_alloc(entry, alignment, size, mark, file, func, line)) + { + return NULL; + } +#endif KcMemoryEntry *new_entry; if ((entry == NULL) && (alignment > 0)) { // アライメント指定でメモリを確保する。 - new_entry = (KcMemoryEntry *)aligned_alloc(alignment, + new_entry = (KcMemoryEntry *)raw_aligned_alloc(alignment, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } else { - new_entry = (KcMemoryEntry *)realloc(entry, + new_entry = (KcMemoryEntry *)raw_realloc(entry, (size_t)(size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING)); } @@ -59,7 +83,7 @@ { entry->mark = KC_MEMORY_DELETED; entry->size = 0; - free(entry); + raw_free(entry); } /** diff --git a/modules/src/kc_ut.c b/modules/src/kc_ut.c index dcf722c..5bb141d 100644 --- a/modules/src/kc_ut.c +++ b/modules/src/kc_ut.c @@ -25,7 +25,7 @@ /** * テスト管理情報 */ -typedef struct +typedef struct KcUtInfo_ { int test_counter; //!< 実施テスト数 int ok_counter; //!< OK 件数 diff --git a/modules/test/src/test_assert.c b/modules/test/src/test_assert.c new file mode 100644 index 0000000..8d523f1 --- /dev/null +++ b/modules/test/src/test_assert.c @@ -0,0 +1,235 @@ +#include +#include + +#include +#include +#include + +// プロトタイプ宣言 +static void test_assert_handler(const char *msg); +static void test_assert_setup(void); +static void test_assert_teardown(void); +static void test_assert_off(void); +static void test_assert_on(void); + +static void test_assert_long(void); +static void test_assert_double(void); +static void test_assert_string(void); +static void test_assert_binary(void); +static void test_assert_null(void); +static void test_assert_true_false(void); + +/** + * assert 単体テストスイート + */ +void suite_assert(void) +{ + KcUt *ut = KcUt_get_instance(); + ut->add(ut, UT_SETUP, "### SETUP (assert) ###", test_assert_setup); + ut->add(ut, UT_TESTCASE, "assert long", test_assert_long); + ut->add(ut, UT_TESTCASE, "assert double", test_assert_double); + ut->add(ut, UT_TESTCASE, "assert string", test_assert_string); + ut->add(ut, UT_TESTCASE, "assert binary", test_assert_binary); + ut->add(ut, UT_TESTCASE, "assert null", test_assert_null); + ut->add(ut, UT_TESTCASE, "assert true/false", test_assert_true_false); + ut->add(ut, UT_TEARDOWN, "### TEARDOWN (assert) ###", test_assert_teardown); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// For assert_handler +// + +/** + * assert テスト用ハンドラが実行されたか否かを表します。 + */ +static bool test_assert_handler_pass = false; + +/** + * ut.c にて使われているハンドラバックアップ用。 + */ +static void (*test_assert_handler_backup)(const char *msg); + +/** + * assert テスト用のハンドラ。 + */ +static void test_assert_handler(const char *msg) +{ + UNUSED_VARIABLE(msg); + test_assert_handler_pass = true; +} + +/** + * assert テストのため、assert_handler を設定します。 + * ※assert にて失敗のテスト実施の際、ut 用のハンドラを使うとテストNGとなるため。 + */ +static void test_assert_setup(void) +{ + test_assert_handler_backup = assert_handler; +} + +/** + * assert テストのため、assert_handler を元にもどします。 + */ +static void test_assert_teardown(void) +{ + assert_handler = test_assert_handler_backup; +} + +/** + * 単体テストのエラー検出を無効にします。 + */ +static void test_assert_off(void) +{ + assert_handler = test_assert_handler; +} + +/** + * 単体テストのエラー検出を有効にします。 + */ +static void test_assert_on(void) +{ + assert_handler = test_assert_handler_backup; +} + +/** + * assert long 単体テスト。 + */ +static void test_assert_long(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(1234, 1234); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(1234, 1235); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert double 単体テスト。 + */ +static void test_assert_double(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 12.34, 0.1); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 12.35, 0.1); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(12.34, 13.35, 0.1); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert string 単体テスト。 + */ +static void test_assert_string(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_equals("ABC", "ABC"); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals("ABC", "DEF"); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert binary 単体テスト。 + */ +static void test_assert_binary(void) +{ + test_assert_handler_pass = false; + char v1[] = {'A', 0, 'B', ' ', 'C', 0x81}; + char v2[] = {'A', 0, 'B', ' ', 'C', 0x81}; + char v3[] = {'A', 0, 'B', 0x19, 'C', 0x81}; + test_assert_off(); + assert_equals(v1, v2, 6); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_equals(v1, v3, 6); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert null 単体テスト。 + */ +static void test_assert_null(void) +{ + const char *ptr = "ABC"; + const char *ptr_null = NULL; + + // not null + test_assert_handler_pass = false; + test_assert_off(); + assert_not_null(ptr); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + // null + test_assert_handler_pass = false; + test_assert_off(); + assert_null(ptr_null); // テスト対象 + test_assert_on(); + assert_false(test_assert_handler_pass); + + // not null error + test_assert_handler_pass = false; + test_assert_off(); + assert_not_null(ptr_null); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + // null error + test_assert_handler_pass = false; + test_assert_off(); + assert_null(ptr); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} + +/** + * assert true/false 単体テスト。 + */ +static void test_assert_true_false(void) +{ + test_assert_handler_pass = false; + test_assert_off(); + assert_true(false); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_false(true); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); + + test_assert_handler_pass = false; + test_assert_off(); + assert_fail(); // テスト対象 + test_assert_on(); + assert_true(test_assert_handler_pass); +} \ No newline at end of file diff --git a/modules/test/src/test_list_array.c b/modules/test/src/test_list_array.c index 5fa1256..1dec74e 100644 --- a/modules/test/src/test_list_array.c +++ b/modules/test/src/test_list_array.c @@ -17,7 +17,7 @@ static void test_list_array_malloc_error(void); /** - * memory_mark 単体テストスイート + * ArrayList 単体テストスイート */ void suite_list_array(void) { @@ -57,7 +57,8 @@ * @process 追加(index 不正) * @process 値取得(サイズ取得あり) * @process 値取得(サイズ取得なし) - * @process 値取得 (index 不正) + * @process 値取得 (index 不正[負の値を指定]) + * @process 値取得 (index 不正[サイズ以上を指定]) */ static void test_list_array_add(void) { @@ -114,7 +115,11 @@ res_val = list->get(list, 3, NULL); assert_equals(40, *res_val); - // 存在しないデータ取得 + // 値取得 (index 不正[負の値を指定]) + res_val = list->get(list, -1, NULL); + assert_null(res_val); + + // 値取得 (index 不正[サイズ以上を指定]) res_val = list->get(list, 4, NULL); assert_null(res_val); @@ -127,8 +132,10 @@ * @process 末尾削除 * @process 中間削除 * @process 先頭削除 - * @process 削除(index 不正) + * @process 削除(index 不正[負の値を指定]) + * @process 削除(index 不正[サイズ以上を指定]) * @process 残っているデータを確認 + * @process 容量調整(初期容量→1/4以下) */ static void test_list_array_remove(void) { @@ -175,7 +182,11 @@ now_size = list->size(list); assert_equals((default_size - 2), now_size); - // 削除(index 不正) + // 削除(index 不正[負の値指定]) + ret = list->remove(list, -1, NULL, NULL); + assert_false(ret); + + // 削除(index 不正[サイズ以上を指定]) ret = list->remove(list, default_size, NULL, NULL); assert_false(ret); @@ -206,10 +217,27 @@ assert_true(is_empty); KcList_delete(list); + + // 容量調整(初期容量→1/4以下) + KcList *big_list = KcList_new_ArrayList(sizeof(int), 12); + int big_val = 10; + // 追加 + bool big_ret = big_list->add(list, 0, &big_val, sizeof(int)); + assert_true(big_ret); + // 削除 (この時、容量調整発生) + big_ret = big_list->remove(list, 0, NULL, NULL); + assert_true(big_ret); + KcList_delete(big_list); } /** * ArrayList データ設定。 + * + * @process 値設定(複数設定) + * @process 値設定(元の値、サイズ取得) + * @process 値設定(元の値取得) + * @process 値設定(不正index[負の値指定]) + * @process 値設定(不正index[サイズ以上指定]) */ static void test_list_array_set(void) { @@ -246,14 +274,53 @@ res_val = list->get(list, 0, NULL); assert_equals(98, *res_val); + // 値設定 (サイズ取得) + set_value = 97; + ret = list->set(list, 0, &set_value, sizeof(int), NULL, &orig_size); + assert_true(ret); + assert_equals((int)sizeof(int), (int)orig_size); + res_val = list->get(list, 0, NULL); + assert_equals(97, *res_val); + now_size = list->size(list); assert_equals(5, now_size); + // 値設定(不正index[負の値指定]) + set_value = 96; + ret = list->set(list, -1, &set_value, sizeof(int), NULL, NULL); + assert_false(ret); + + // 値設定(不正index[サイズ以上指定]) + set_value = 95; + ret = list->set(list, 5, &set_value, sizeof(int), NULL, NULL); + assert_false(ret); + KcList_delete(list); } /** * ArrayList 検索。 + * + * @process リスト {10, 30, 50, 40, 20, 10, 30, 60, 20 } を構築する。 + * @result リストが構築されること + * + * @process is_contains でリストに含まれる値を確認する。 + * @result true が返されること。 + * + * @process index_of で各インデックス位置を取得する。 + * @result 先頭からのインデックス位置が正しく取得されること。 + * + * @process last_index_of で各インデックス位置を取得する。 + * @result 末尾からのインデックス位置が正しく取得されること。 + * + * @process リストに含まれない値を指定して、is_contains を実行する。 + * @result false が返されること。 + * + * @process リストに含まれない値を指定して、index_of を実行する。 + * @result -1 が返されること。 + * + * @process リストに含まれない値を指定して、last_index_of を実行する。 + * @result -1 が返されること。 */ static void test_list_array_search(void) { @@ -321,6 +388,15 @@ /** * ArrayList ソート。 + * + * @process リスト {10, 30, 50, 40, 20, 10, 30, 60, 20} を構築する。 + * @result リストが構築されること。 + * + * @process 昇順用コンパレータを渡し、ソートする。 + * @result リストがソートされること。 + * + * @process Iterator を取得する。 + * @result Iterator にて順次値が取得できること。 */ static void test_list_array_sort(void) { @@ -342,6 +418,7 @@ list->sort(list, test_list_array_sort_comparator, "ABCDEFG"); int sorted_vals[] = {10, 10, 20, 20, 30, 30, 40, 50, 60}; + // Iterator にて値を取得&確認 KcIterator *ite = list->iterator(list, 0); int sorted_index = 0; while (ite->hasNext(ite)) @@ -368,12 +445,71 @@ KcList_delete(list); } +#include "ut.h" +static int UT_ListArray_can_alloc_counter = 0; +static bool UT_ListArray_can_alloc( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) +{ + UNUSED_VARIABLE(entry); + UNUSED_VARIABLE(alignment); + UNUSED_VARIABLE(size); + UNUSED_VARIABLE(mark); + UNUSED_VARIABLE(file); + UNUSED_VARIABLE(func); + UNUSED_VARIABLE(line); + + UT_ListArray_can_alloc_counter--; + if (UT_ListArray_can_alloc_counter < 0) + { + return false; + } + return true; +} + /** * ArrayList メモリ確保失敗。 */ - static void test_list_array_malloc_error(void) { - KcList *list = KcList_new_ArrayList(sizeof(int), -1); + // ArrayList のメモリ確保失敗 + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + KcList *list = KcList_new_ArrayList(sizeof(int), 3); + _UT_KcMemory_can_alloc = NULL; assert_null(list); + + // ArrayList のデータ用メモリ確保失敗 + UT_ListArray_can_alloc_counter = 1; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + list = KcList_new_ArrayList(sizeof(int), 3); + _UT_KcMemory_can_alloc = NULL; + assert_null(list); + + // Array Size 拡張時のメモリ確保失敗 + list = KcList_new_ArrayList(sizeof(int), 3); + int val = 10; + list->add(list, 0, &val, sizeof(int)); + list->add(list, 1, &val, sizeof(int)); + list->add(list, 2, &val, sizeof(int)); + + bool ret; + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + // size 以上 + ret = list->add(list, 3, &val, sizeof(int)); + assert_false(ret); + // 0 指定 + ret = list->add(list, 0, &val, sizeof(int)); + assert_false(ret); + _UT_KcMemory_can_alloc = NULL; + + // Iterator 生成時のメモリ確保失敗 + UT_ListArray_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_ListArray_can_alloc; + KcIterator *ite = list->iterator(list, 0); + _UT_KcMemory_can_alloc = NULL; + assert_null(ite); + + KcList_delete(list); } \ No newline at end of file diff --git a/modules/test/src/test_memory.c b/modules/test/src/test_memory.c index ddf6d0d..b8b953e 100644 --- a/modules/test/src/test_memory.c +++ b/modules/test/src/test_memory.c @@ -1,33 +1,314 @@ #include +#include + #include #include #include +#include -static void test_1(void) -{ - assert_equals("a", "a"); -} +#include "ut.h" -static void test_2(void) -{ - assert_equals(1, 2); -} -static void setup(void) -{ - // NOP -} -static void test_3(void) -{ - assert_true(1 == 1); -} +// プロトタイプ宣言 +static void test_memory_start(void); +static void test_memory_dump(void); +static void test_memory_set_listener(void); +static void test_memory_entries(void); +static void test_memory_freeif(void); +static void test_memory_aligned(void); +static void test_memory_calloc(void); +static void test_memory_realloc(void); +static void test_memory_raw_xxxxx(void); -void test_memory(void) +/** + * memory_mark 単体テストスイート + */ +void suite_memory(void) { KcUt *ut = KcUt_get_instance(); - ut->add(ut, UT_SETUP, "SETUP", setup); - ut->add(ut, UT_TESTCASE, "テスト1", test_1); - ut->add(ut, UT_SETUP, "SETUP", setup); - ut->add(ut, UT_TESTCASE, "テスト2", test_2); - ut->add(ut, UT_SETUP, "SETUP", setup); - ut->add(ut, UT_TESTCASE, "テスト3", test_3); + ut->add(ut, UT_TESTCASE, "memory start", test_memory_start); + ut->add(ut, UT_TESTCASE, "memory dump", test_memory_dump); + ut->add(ut, UT_TESTCASE, "memory set_listener", test_memory_set_listener); + ut->add(ut, UT_TESTCASE, "memory entries", test_memory_entries); + ut->add(ut, UT_TESTCASE, "memory freeif", test_memory_freeif); + ut->add(ut, UT_TESTCASE, "memory aligned", test_memory_aligned); + ut->add(ut, UT_TESTCASE, "memory calloc", test_memory_calloc); + ut->add(ut, UT_TESTCASE, "memory realloc", test_memory_realloc); + ut->add(ut, UT_TESTCASE, "memory raw_xxxxx", test_memory_raw_xxxxx); +} + +/** + * メモリ管理開始。 + * + * @process メモリ管理開始(情報詳細出力あり) + * @process メモリ管理開始(情報詳細出力無し) + */ +static void test_memory_start(void) +{ + // NOP + // ut.c にて、下記実施 + // KcMemory_start(false); + // KcMemory_start(true); +} + +extern void KcMemory_dump_leak(void); +static bool test_memory_dump_handler(const char *data) +{ + printf("%s", data); + return true; +} + +/** + * 確保中のメモリ情報出力。 + * + * @process dump を実行する。 + * @result 確保中のメモリ情報が出力されること。 + * + * @param dump_leak を実行する。 + * @result メモリリーク情報が出力されること。 + * + * @process 確保しているメモリがない状態で dump を実行する。 + * @result メモリ情報が出力されないこと。 + * + * @process 出力用ハンドラを指定してメモリをダンプする。 + * @result ハンドラに対してメモリ情報が渡されること。 + */ +static void test_memory_dump(void) +{ + int *val = (int *)malloc(sizeof(int)); + *val = 10; + + printf("---DUMP---\n"); + KcMemory_dump(); + + printf("---DUMP LEAK---\n"); + KcMemory_dump_leak(); + + free(val); + printf("---DUMP---\n"); + KcMemory_dump(); + + val = (int *)malloc(sizeof(int)); + *val = 20; + kc_memory_manager->dump( + test_memory_dump_handler, 16, true, true, 8192); + free(val); +} + +/** + * リスナ設定(NULL指定)。 + * + * @process リスナを登録する。(NULL指定) + * @result リスナが設定されないこと。 + */ +static void test_memory_set_listener(void) +{ + KcMemoryListener listener; + listener.allocate = NULL; + listener.free = NULL; + listener.error = NULL; + kc_memory_manager->set_listener(&listener); +} + +// ハンドラ実行用 +static int test_memory_entries_handler_counter = 0; +static int test_memory_entries_handler_values[10]; +static int test_memory_entries_handler_can_loop = 0; +static bool test_memory_entries_handler(const KcMemoryEntry *entry, void *info) +{ + assert_equals( + test_memory_entries_handler_values[test_memory_entries_handler_counter], + *((int *)entry->data)); + test_memory_entries_handler_counter++; + test_memory_entries_handler_can_loop--; + assert_equals("X", (const char *)info); + if (test_memory_entries_handler_can_loop <= 0) + { + return false; + } + return true; +} +/** + * メモリエントリハンドラ実行 + */ +static void test_memory_entries(void) +{ + int *val1 = (int *)malloc(sizeof(int)); + int *val2 = (int *)malloc(sizeof(int)); + int *val3 = (int *)malloc(sizeof(int)); + *val1 = 123; + *val2 = 456; + *val3 = 789; + test_memory_entries_handler_values[0] = 123; + test_memory_entries_handler_values[1] = 456; + test_memory_entries_handler_values[2] = 789; + + // 途中中断パターン + test_memory_entries_handler_counter = 0; + test_memory_entries_handler_can_loop = 2; + kc_memory_manager->entries(test_memory_entries_handler, "X"); + assert_equals(2, test_memory_entries_handler_counter); + + // 全てダンプパターン + test_memory_entries_handler_counter = 0; + test_memory_entries_handler_can_loop = 999; + kc_memory_manager->entries(test_memory_entries_handler, "X"); + assert_equals(3, test_memory_entries_handler_counter); + + free(val1); + free(val2); + free(val3); +} + +bool test_memory_freeif_handler(const KcMemoryEntry *entry, void *info) +{ + assert_equals("FREEIF", (const char *)info); + if (strcmp("test_memory_freeif", entry->func) == 0) + { + int *data = (int *)entry->data; + if (*data == 456) + { + return true; + } + } + return false; +} + +/** + * 強制メモリ破棄 + */ +static void test_memory_freeif(void) +{ + int *val1 = (int *)malloc(sizeof(int)); + int *val2 = (int *)malloc(sizeof(int)); + int *val3 = (int *)malloc(sizeof(int)); + *val1 = 123; + *val2 = 456; + *val3 = 789; + test_memory_entries_handler_values[0] = 123; + test_memory_entries_handler_values[1] = 456; + test_memory_entries_handler_values[2] = 789; + + kc_memory_manager->freeif(test_memory_freeif_handler, "FREEIF"); + free(val1); + // free(val2); + free(val3); +} + +// メモリ確保失敗動作確認用 +static int UT_Memory_can_alloc_counter = 0; +static bool UT_Memory_can_alloc( + KcMemoryEntry *entry, size_t alignment, size_t size, + KcMemoryMark mark, const char *file, const char *func, int line) +{ + UNUSED_VARIABLE(entry); + UNUSED_VARIABLE(alignment); + UNUSED_VARIABLE(size); + UNUSED_VARIABLE(mark); + UNUSED_VARIABLE(file); + UNUSED_VARIABLE(func); + UNUSED_VARIABLE(line); + + UT_Memory_can_alloc_counter--; + if (UT_Memory_can_alloc_counter < 0) + { + return false; + } + return true; +} +/** + * アライメント指定メモリ確保 + * + * @process aligned_alloc によりメモリを確保する。 + * @result メモリが確保されること。 + * + * @process メモリ確保が失敗する状態で、aligned_alloc を実行する。 + * @result メモリ確保に失敗し、NULL が返されること。 + */ +static void test_memory_aligned(void) +{ + int *val = (int *)aligned_alloc(sizeof(long long), sizeof(int)); + assert_not_null(val); + *val = 123; + free(val); + + UT_Memory_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_Memory_can_alloc; + val = (int *)aligned_alloc(sizeof(long long), sizeof(int)); + assert_null(val); + _UT_KcMemory_can_alloc = NULL; +} + +/** + * calloc によるメモリ確保 + * + * @process calloc によりメモリを確保する。 + * @result メモリが確保されること。内容がすべて 0 のこと。 + * + * @process メモリ確保が失敗する状態で、calloc を実行する。 + * @result メモリ確保に失敗し、NULL が返されること。 + */ +static void test_memory_calloc(void) +{ + int *val = (int *)calloc(10, sizeof(int)); + for (int i = 0; i < 10; i++) + { + assert_equals(0, val[i]); + } + free(val); + + UT_Memory_can_alloc_counter = 0; + _UT_KcMemory_can_alloc = UT_Memory_can_alloc; + val = (int *)calloc(10, sizeof(int)); + assert_null(val); + _UT_KcMemory_can_alloc = NULL; +} + +/** + * realloc によるメモリ確保 + * + * @process NULL を指定して、realloc によりメモリを確保する。 + * @result メモリが確保されること。 + * + * @process メモリ確保が失敗する状態で、calloc を実行する。 + * @result メモリ確保に失敗し、NULL が返されること。 + */ +static void test_memory_realloc(void) +{ + // NULL 指定 + int *val = realloc(NULL, sizeof(int)); + assert_not_null(val); + *val = 123; + free(val); + + // 管理外メモリによる realloc + int *unmng_ptr = raw_malloc(sizeof(int) * 2); + unmng_ptr[0] = 123; + unmng_ptr[1] = 456; + assert_equals(123, unmng_ptr[0]); + assert_equals(456, unmng_ptr[1]); + + int *mng_ptr = (int *)realloc(unmng_ptr, sizeof(int) * 3); + assert_null(mng_ptr); + free(unmng_ptr); +} + +/** + * raw_xxxxx 関数動作確認。 + */ +static void test_memory_raw_xxxxx(void) +{ + int *ptr = (int *)raw_malloc(sizeof(int)); + assert_not_null(ptr); + raw_free(ptr); + + ptr = raw_aligned_alloc(sizeof(int), sizeof(int)); + assert_not_null(ptr); + raw_free(ptr); + + ptr = raw_calloc(10, sizeof(int)); + assert_not_null(ptr); + + ptr = raw_realloc(ptr, 20); + assert_not_null(ptr); + raw_free(ptr); } diff --git a/modules/test/src/ut.c b/modules/test/src/ut.c index 41f9017..ed34d55 100644 --- a/modules/test/src/ut.c +++ b/modules/test/src/ut.c @@ -3,26 +3,22 @@ #include #include -extern void suite_lock_guard(void); -extern void suite_memory_dump(void); -extern void suite_memory_entry(void); -extern void suite_memory_listener(void); -extern void suite_memory_mark(void); -extern void suite_list_array(void); +#include "ut.h" int main(void) { // UT Setup - // test_list(); - // test_memory(); + suite_assert(); + suite_list_array(); suite_lock_guard(); suite_memory_dump(); suite_memory_entry(); suite_memory_listener(); suite_memory_mark(); - suite_list_array(); + suite_memory(); KcMemory_start(false); + KcMemory_start(true); KcUt *ut = KcUt_get_instance(); ut->run(ut); diff --git a/modules/test/src/ut.h b/modules/test/src/ut.h new file mode 100644 index 0000000..39d014e --- /dev/null +++ b/modules/test/src/ut.h @@ -0,0 +1,20 @@ +#ifndef UT_H +#define UT_H + +#include +#include + +extern void suite_assert(void); +extern void suite_list_array(void); +extern void suite_lock_guard(void); +extern void suite_memory_dump(void); +extern void suite_memory_entry(void); +extern void suite_memory_listener(void); +extern void suite_memory_mark(void); +extern void suite_memory(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); + +#endif // UT_H \ No newline at end of file